From a35754713436c688e8e82691433c8088922da0e4 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 13 May 2024 21:37:33 +0000 Subject: [PATCH 01/47] Adds basic disk polling mechanism to 'discover' checks --- Cargo.lock | 153 ++++++++++ Cargo.toml | 2 - bin/agent-data-plane/src/main.rs | 5 +- lib/saluki-components/Cargo.toml | 1 + .../src/sources/checks/mod.rs | 272 ++++++++++++++++++ lib/saluki-components/src/sources/mod.rs | 3 + 6 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 lib/saluki-components/src/sources/checks/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6cad180d..6375aa93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,19 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "async-channel" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a" +dependencies = [ + "concurrent-queue", + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-compression" version = "0.4.10" @@ -96,6 +109,28 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -118,6 +153,12 @@ dependencies = [ "syn", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.80" @@ -129,6 +170,16 @@ dependencies = [ "syn", ] +[[package]] +name = "async-walkdir" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f6338023cbfc0555eccb8e83d3d4dcf1183b51ca9140a03b1dbb8a559193db" +dependencies = [ + "async-fs", + "futures-lite", +] + [[package]] name = "atomic" version = "0.6.0" @@ -138,6 +189,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -298,6 +355,20 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bstr" version = "0.2.17" @@ -388,6 +459,15 @@ dependencies = [ "cc", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.8" @@ -550,6 +630,48 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.0", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -662,6 +784,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -1526,6 +1661,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "paste" version = "1.0.15" @@ -1658,6 +1799,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "portable-atomic" version = "1.6.0" @@ -2089,6 +2241,7 @@ dependencies = [ "ahash", "async-compression", "async-trait", + "async-walkdir", "bitmask-enum", "bytes", "datadog-protos", diff --git a/Cargo.toml b/Cargo.toml index b7fdc2ed..08df9748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,10 +83,8 @@ tonic = { version = "0.11", default-features = false } arc-swap = { version = "1.7.1", default-features = false } async-stream = { version = "0.3.5", default-features = false } futures = { version = "0.3.30", default-features = false } -oci-spec = { version = "0.6.5", default-features = false } prost-types = { version = "0.12", default-features = false } serde_json = { version = "1.0.116", default-features = false } -headers = { version = "0.3", default-features = false } rustls-pemfile = { version = "2", default-features = false } tokio-rustls = { version = "0.25.0", default-features = false } anyhow = { version = "1", default-features = false } diff --git a/bin/agent-data-plane/src/main.rs b/bin/agent-data-plane/src/main.rs index ba55e823..a6e15316 100644 --- a/bin/agent-data-plane/src/main.rs +++ b/bin/agent-data-plane/src/main.rs @@ -10,7 +10,7 @@ use std::time::{Duration, Instant}; use saluki_components::{ destinations::DatadogMetricsConfiguration, - sources::{DogStatsDConfiguration, InternalMetricsConfiguration}, + sources::{ChecksConfiguration, DogStatsDConfiguration, InternalMetricsConfiguration}, transforms::{ AggregateConfiguration, ChainedConfiguration, HostEnrichmentConfiguration, OriginEnrichmentConfiguration, }, @@ -80,10 +80,13 @@ async fn run(started: Instant) -> Result<(), GenericError> { .with_transform_builder(origin_enrichment_config); let dd_metrics_config = DatadogMetricsConfiguration::from_configuration(&configuration)?; + let check_config = ChecksConfiguration::from_configuration(&configuration)?; + let mut blueprint = TopologyBlueprint::default(); blueprint .add_source("dsd_in", dsd_config)? .add_source("internal_metrics_in", int_metrics_config)? + .add_source("checks", check_config)? .add_transform("dsd_agg", dsd_agg_config)? .add_transform("internal_metrics_agg", int_metrics_agg_config)? .add_transform("enrich", enrich_config)? diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 08a13eb1..74d8190f 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -46,3 +46,4 @@ tokio-util = { workspace = true } tower = { workspace = true } tracing = { workspace = true } url = { workspace = true } +async-walkdir = "1.0.0" diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs new file mode 100644 index 00000000..95a35106 --- /dev/null +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -0,0 +1,272 @@ +use async_trait::async_trait; +use async_walkdir::{DirEntry, Filtering, WalkDir}; +use futures::StreamExt as _; +use saluki_config::GenericConfiguration; +use saluki_core::{ + components::{Source, SourceBuilder, SourceContext}, + prelude::ErasedError, + topology::{ + shutdown::{DynamicShutdownCoordinator, DynamicShutdownHandle}, + OutputDefinition, + }, +}; +use saluki_event::DataType; +use serde::Deserialize; +use snafu::Snafu; +use std::path::{Path, PathBuf}; +use std::{collections::HashSet, io}; +use tokio::{select, sync::mpsc}; +use tracing::{debug, info}; // Import the Display trait + +#[derive(Debug, Snafu)] +#[snafu(context(suffix(false)))] +enum Error { + #[snafu(display("Directory incorrect"))] + DirectoryIncorrect { source: io::Error }, +} + +/// Checks source. +/// +/// Scans a directory for check configurations and emits them as things to run. +#[derive(Deserialize)] +pub struct ChecksConfiguration { + /// The size of the buffer used to receive messages into, in bytes. + /// + /// Payloads cannot exceed this size, or they will be truncated, leading to discarded messages. + /// + /// Defaults to 8192 bytes. + #[serde(default = "default_check_config_dir")] + check_config_dir: String, +} + +fn default_check_config_dir() -> String { + "./conf.d".to_string() +} + +struct DirCheckListener { + base_path: PathBuf, + known_check_paths: Vec, + // These could all be oneshot channels I think + // but maybe buffering is useful + new_path_tx: mpsc::Sender, + deleted_path_tx: mpsc::Sender, + new_path_rx: Option>, + deleted_path_rx: Option>, +} + +struct DirCheckListenerContext { + shutdown_handle: DynamicShutdownHandle, + listener: DirCheckListener, +} + +impl DirCheckListener { + /// Constructs a new `Listener` that will monitor the specified path. + pub fn from_path>(path: P) -> Result { + let path_ref = path.as_ref(); + if !path_ref.exists() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), + }); + } + if !path_ref.is_dir() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new(io::ErrorKind::NotFound, "Path is not a directory"), + }); + } + + let (new_paths_tx, new_paths_rx) = mpsc::channel(100); + let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); + Ok(DirCheckListener { + base_path: path.as_ref().to_path_buf(), + known_check_paths: Vec::new(), + new_path_tx: new_paths_tx, + deleted_path_tx: deleted_paths_tx, + new_path_rx: Some(new_paths_rx), + deleted_path_rx: Some(deleted_paths_rx), + }) + } + + pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { + (self.new_path_rx.take().unwrap(), self.deleted_path_rx.take().unwrap()) + } + + async fn update_check_entities(&mut self) { + let new_check_paths = self.get_check_entities().await; + let current: HashSet = HashSet::from_iter(self.known_check_paths.clone().into_iter()); + let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); + + //let new_entities: Vec = new.difference(¤t).collect::>().map(|e| e.clone()); + //let deleted_entities: Vec = current.difference(&new).cloned().collect::>(); + + for entity in new.difference(¤t) { + self.known_check_paths.push(entity.clone()); + // todo error handling + self.new_path_tx.send(entity.clone()).await; + } + for entity in current.difference(&new) { + self.known_check_paths.retain(|e| e != entity); + // todo error handling + self.deleted_path_tx.send(entity.clone()).await; + } + } + + /// Retrieves all check entities from the base path that match the required check formats. + pub async fn get_check_entities(&self) -> Vec { + let entries = WalkDir::new(&self.base_path).filter(|entry| async move { + if let Some(true) = entry.path().file_name().map(|f| f.to_string_lossy().starts_with('.')) { + return Filtering::IgnoreDir; + } + if is_check_entity(&entry).await { + Filtering::Continue + } else { + Filtering::Ignore + } + }); + + entries + .filter_map(|e| async move { + match e { + Ok(entry) => Some(entry.path().to_path_buf()), + Err(e) => { + eprintln!("Error traversing files: {}", e); + None + } + } + }) + .collect() + .await + } +} + +/// Determines if a directory entry is a valid check entity based on defined patterns. +async fn is_check_entity(entry: &DirEntry) -> bool { + let path = entry.path(); + let file_type = entry.file_type().await.expect("Couldn't get file type"); + if file_type.is_file() { + // Matches `./mycheck.yaml` + if path.extension().unwrap_or_default() == "yaml" { + return path + .file_stem() + .unwrap_or_default() + .to_str() + .unwrap_or("") + .ends_with("check"); + } + } else if file_type.is_dir() { + // Matches `./mycheck.d/conf.yaml` + let conf_path = path.join("conf.yaml"); + return conf_path.exists() + && path + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or("") + .ends_with("check.d"); + } + false +} + +impl ChecksConfiguration { + /// Creates a new `DogStatsDConfiguration` from the given configuration. + pub fn from_configuration(config: &GenericConfiguration) -> Result { + Ok(config.as_typed()?) + } + + async fn build_listeners(&self) -> Result, Error> { + let mut listeners = Vec::new(); + + let listener = DirCheckListener::from_path(&self.check_config_dir)?; + + listeners.push(listener); + + Ok(listeners) + } +} + +#[async_trait] +impl SourceBuilder for ChecksConfiguration { + async fn build(&self) -> Result, Box> { + let listeners = self.build_listeners().await?; + + Ok(Box::new(Checks { listeners })) + } + + fn outputs(&self) -> &[OutputDefinition] { + static OUTPUTS: &[OutputDefinition] = &[OutputDefinition::default_output(DataType::Metric)]; + + OUTPUTS + } +} + +pub struct Checks { + listeners: Vec, +} + +#[async_trait] +impl Source for Checks { + async fn run(mut self: Box, mut context: SourceContext) -> Result<(), ()> { + let global_shutdown = context + .take_shutdown_handle() + .expect("should never fail to take shutdown handle"); + + let mut listener_shutdown_coordinator = DynamicShutdownCoordinator::default(); + + // For each listener, spawn a dedicated task to run it. + for listener in self.listeners { + let listener_context = DirCheckListenerContext { + shutdown_handle: listener_shutdown_coordinator.register(), + listener, + }; + + tokio::spawn(process_listener(context.clone(), listener_context)); + } + + info!("Check source started."); + + // Wait for the global shutdown signal, then notify listeners to shutdown. + global_shutdown.await; + info!("Stopping Check source..."); + + listener_shutdown_coordinator.shutdown().await; + + info!("Check source stopped."); + + Ok(()) + } +} + +async fn process_listener(source_context: SourceContext, listener_context: DirCheckListenerContext) { + let DirCheckListenerContext { + shutdown_handle, + mut listener, + } = listener_context; + tokio::pin!(shutdown_handle); + + let mut stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); + + info!("Check listener started."); + // every 1 sec check for new check entities + let (mut new_entities, mut deleted_entities) = listener.subscribe(); + loop { + select! { + _ = &mut shutdown_handle => { + debug!("Received shutdown signal. Waiting for existing stream handlers to finish..."); + break; + } + _ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => { + debug!("Checking for new check entities..."); + listener.update_check_entities().await; + } + Some(new_entity) = new_entities.recv() => { + debug!("Received new check entity {}", new_entity.display()); + } + Some(deleted_entity) = deleted_entities.recv() => { + debug!("Received deleted check entity {}", deleted_entity.display()); + } + } + } + + stream_shutdown_coordinator.shutdown().await; + + info!("Check listener stopped."); +} diff --git a/lib/saluki-components/src/sources/mod.rs b/lib/saluki-components/src/sources/mod.rs index 95e5270f..f64cd43c 100644 --- a/lib/saluki-components/src/sources/mod.rs +++ b/lib/saluki-components/src/sources/mod.rs @@ -3,3 +3,6 @@ pub use self::dogstatsd::DogStatsDConfiguration; mod internal_metrics; pub use self::internal_metrics::InternalMetricsConfiguration; + +mod checks; +pub use self::checks::ChecksConfiguration; From 62d0962950d35c3c233d63b3294ff0c6ea694c57 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 13 May 2024 21:53:44 +0000 Subject: [PATCH 02/47] Corrects the topology and the 'is check config' check --- bin/agent-data-plane/src/main.rs | 14 +++++++++++--- lib/saluki-components/src/sources/checks/mod.rs | 13 ++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/bin/agent-data-plane/src/main.rs b/bin/agent-data-plane/src/main.rs index a6e15316..14176954 100644 --- a/bin/agent-data-plane/src/main.rs +++ b/bin/agent-data-plane/src/main.rs @@ -69,8 +69,14 @@ async fn run(started: Instant) -> Result<(), GenericError> { // and a Datadog Metrics destination that forwards aggregated buckets to the Datadog Platform. let dsd_config = DogStatsDConfiguration::from_configuration(&configuration)?; let dsd_agg_config = AggregateConfiguration::from_window(Duration::from_secs(10)).with_context_limit(15500); + // Aggregation currently only supports time windows + // we'll need to add support for a "check sampler" like aggregator + // basically this _only_ does metric aggregation rules, no time rules. + // flushing is similar to current behavior, just need to think about 'counter' resets + // for now, just use time windows, if we make each window shorter than the check interval, it should be fine + let checks_agg_config = AggregateConfiguration::from_window(Duration::from_secs(5)); let int_metrics_config = InternalMetricsConfiguration; - let int_metrics_agg_config = AggregateConfiguration::from_window(Duration::from_secs(10)); + let int_metrics_agg_config = AggregateConfiguration::from_window(Duration::from_secs(10)).flush_open_windows(true); let host_enrichment_config = HostEnrichmentConfiguration::from_environment_provider(env_provider.clone()); let origin_enrichment_config = OriginEnrichmentConfiguration::from_configuration(&configuration)? @@ -86,14 +92,16 @@ async fn run(started: Instant) -> Result<(), GenericError> { blueprint .add_source("dsd_in", dsd_config)? .add_source("internal_metrics_in", int_metrics_config)? - .add_source("checks", check_config)? + .add_source("checks_in", check_config)? .add_transform("dsd_agg", dsd_agg_config)? + .add_transform("checks_agg", checks_agg_config)? .add_transform("internal_metrics_agg", int_metrics_agg_config)? .add_transform("enrich", enrich_config)? .add_destination("dd_metrics_out", dd_metrics_config)? .connect_component("dsd_agg", ["dsd_in"])? + .connect_component("checks_agg", ["checks_in"])? .connect_component("internal_metrics_agg", ["internal_metrics_in"])? - .connect_component("enrich", ["dsd_agg", "internal_metrics_agg"])? + .connect_component("enrich", ["dsd_agg", "internal_metrics_agg", "checks_agg"])? .connect_component("dd_metrics_out", ["enrich"])?; let built_topology = blueprint.build().await?; let running_topology = built_topology.spawn().await?; diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 95a35106..dc98ed56 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -95,9 +95,6 @@ impl DirCheckListener { let current: HashSet = HashSet::from_iter(self.known_check_paths.clone().into_iter()); let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); - //let new_entities: Vec = new.difference(¤t).collect::>().map(|e| e.clone()); - //let deleted_entities: Vec = current.difference(&new).cloned().collect::>(); - for entity in new.difference(¤t) { self.known_check_paths.push(entity.clone()); // todo error handling @@ -144,14 +141,7 @@ async fn is_check_entity(entry: &DirEntry) -> bool { let file_type = entry.file_type().await.expect("Couldn't get file type"); if file_type.is_file() { // Matches `./mycheck.yaml` - if path.extension().unwrap_or_default() == "yaml" { - return path - .file_stem() - .unwrap_or_default() - .to_str() - .unwrap_or("") - .ends_with("check"); - } + return path.extension().unwrap_or_default() == "yaml"; } else if file_type.is_dir() { // Matches `./mycheck.d/conf.yaml` let conf_path = path.join("conf.yaml"); @@ -259,6 +249,7 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh } Some(new_entity) = new_entities.recv() => { debug!("Received new check entity {}", new_entity.display()); + // TODO - try to start running this check } Some(deleted_entity) = deleted_entities.recv() => { debug!("Received deleted check entity {}", deleted_entity.display()); From 9fc63a6673888e14a743576b731bf92a083daf86 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 13 May 2024 21:56:10 +0000 Subject: [PATCH 03/47] Some comments --- lib/saluki-components/src/sources/checks/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index dc98ed56..d4def12f 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -16,7 +16,7 @@ use snafu::Snafu; use std::path::{Path, PathBuf}; use std::{collections::HashSet, io}; use tokio::{select, sync::mpsc}; -use tracing::{debug, info}; // Import the Display trait +use tracing::{debug, info}; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -30,11 +30,7 @@ enum Error { /// Scans a directory for check configurations and emits them as things to run. #[derive(Deserialize)] pub struct ChecksConfiguration { - /// The size of the buffer used to receive messages into, in bytes. - /// - /// Payloads cannot exceed this size, or they will be truncated, leading to discarded messages. - /// - /// Defaults to 8192 bytes. + /// The directory containing the check configurations. #[serde(default = "default_check_config_dir")] check_config_dir: String, } @@ -244,15 +240,22 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh break; } _ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => { - debug!("Checking for new check entities..."); listener.update_check_entities().await; } Some(new_entity) = new_entities.recv() => { debug!("Received new check entity {}", new_entity.display()); // TODO - try to start running this check + // So what component will be responsible for "running" the check? + // There needs to be a "check registry" component that finds the checks + // in 'checks.d' as well. + // This is almost identical to the 'DirCheckListener' in that it just looks + // at a directory and finds anything ending in '.py' + // This component doesn't exist yet } Some(deleted_entity) = deleted_entities.recv() => { debug!("Received deleted check entity {}", deleted_entity.display()); + // TODO - try to stop running this check + // (low priority) } } } From 70f46906359a993a938b63a755dd09a2cbc31781 Mon Sep 17 00:00:00 2001 From: Remy Mathieu Date: Tue, 14 May 2024 16:32:38 +0200 Subject: [PATCH 04/47] Reads the checks YAML configuration file and looks for the .py in the same dir. --- Cargo.lock | 1 + Cargo.toml | 1 + bin/agent-data-plane/src/main.rs | 2 +- lib/saluki-components/Cargo.toml | 1 + .../src/sources/checks/mod.rs | 125 +++++++++++++++++- 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6375aa93..fb20e5e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2270,6 +2270,7 @@ dependencies = [ "saluki-event", "saluki-io", "serde", + "serde_yaml", "slab", "snafu", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 08df9748..e3e18bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ async-stream = { version = "0.3.5", default-features = false } futures = { version = "0.3.30", default-features = false } prost-types = { version = "0.12", default-features = false } serde_json = { version = "1.0.116", default-features = false } +serde_yaml = { version = "0.9.34", default-features = false } rustls-pemfile = { version = "2", default-features = false } tokio-rustls = { version = "0.25.0", default-features = false } anyhow = { version = "1", default-features = false } diff --git a/bin/agent-data-plane/src/main.rs b/bin/agent-data-plane/src/main.rs index 14176954..af7763ec 100644 --- a/bin/agent-data-plane/src/main.rs +++ b/bin/agent-data-plane/src/main.rs @@ -74,7 +74,7 @@ async fn run(started: Instant) -> Result<(), GenericError> { // basically this _only_ does metric aggregation rules, no time rules. // flushing is similar to current behavior, just need to think about 'counter' resets // for now, just use time windows, if we make each window shorter than the check interval, it should be fine - let checks_agg_config = AggregateConfiguration::from_window(Duration::from_secs(5)); + let checks_agg_config = AggregateConfiguration::from_window(Duration::from_secs(15)); let int_metrics_config = InternalMetricsConfiguration; let int_metrics_agg_config = AggregateConfiguration::from_window(Duration::from_secs(10)).flush_open_windows(true); diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 74d8190f..34de9c9e 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -39,6 +39,7 @@ protobuf = { workspace = true } quanta = { workspace = true } rustls = { workspace = true, features = ["aws_lc_rs", "logging", "tls12"] } serde = { workspace = true } +serde_yaml = { workspace = true } slab = { workspace = true } snafu = { workspace = true } tokio = { workspace = true, features = ["io-util", "macros", "net", "rt", "rt-multi-thread", "signal", "sync"] } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index d4def12f..8743d601 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -16,13 +16,15 @@ use snafu::Snafu; use std::path::{Path, PathBuf}; use std::{collections::HashSet, io}; use tokio::{select, sync::mpsc}; -use tracing::{debug, info}; +use tracing::{debug, info, error}; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] enum Error { #[snafu(display("Directory incorrect"))] DirectoryIncorrect { source: io::Error }, + CantReadConfiguration { source: serde_yaml::Error }, + NoSourceAvailable { reason: String }, } /// Checks source. @@ -39,6 +41,10 @@ fn default_check_config_dir() -> String { "./conf.d".to_string() } +fn default_check_implementation_dir() -> String { + "./checks.d".to_string() +} + struct DirCheckListener { base_path: PathBuf, known_check_paths: Vec, @@ -55,6 +61,30 @@ struct DirCheckListenerContext { listener: DirCheckListener, } +// checks configuration + +#[derive(Debug, Deserialize)] +struct CheckInstance { + configuration: CheckInstanceConfiguration, + source_code_relative_filepath: PathBuf, +} +#[derive(Debug, Deserialize)] +struct CheckInstanceConfiguration { + min_collection_interval_ms: u32, +} + +// YAML configuration + +#[derive(Debug, Deserialize)] +struct YamlCheckConfiguration { + instance: Vec +} + +#[derive(Debug, Deserialize)] +struct YamlCheckInstance { + min_collection_interval: u32, +} + impl DirCheckListener { /// Constructs a new `Listener` that will monitor the specified path. pub fn from_path>(path: P) -> Result { @@ -138,7 +168,9 @@ async fn is_check_entity(entry: &DirEntry) -> bool { if file_type.is_file() { // Matches `./mycheck.yaml` return path.extension().unwrap_or_default() == "yaml"; - } else if file_type.is_dir() { + } + + if file_type.is_dir() { // Matches `./mycheck.d/conf.yaml` let conf_path = path.join("conf.yaml"); return conf_path.exists() @@ -153,7 +185,7 @@ async fn is_check_entity(entry: &DirEntry) -> bool { } impl ChecksConfiguration { - /// Creates a new `DogStatsDConfiguration` from the given configuration. + /// Creates a new `ChecksConfiguration` from the given configuration. pub fn from_configuration(config: &GenericConfiguration) -> Result { Ok(config.as_typed()?) } @@ -228,7 +260,7 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh } = listener_context; tokio::pin!(shutdown_handle); - let mut stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); + let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); info!("Check listener started."); // every 1 sec check for new check entities @@ -243,7 +275,6 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh listener.update_check_entities().await; } Some(new_entity) = new_entities.recv() => { - debug!("Received new check entity {}", new_entity.display()); // TODO - try to start running this check // So what component will be responsible for "running" the check? // There needs to be a "check registry" component that finds the checks @@ -251,6 +282,31 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh // This is almost identical to the 'DirCheckListener' in that it just looks // at a directory and finds anything ending in '.py' // This component doesn't exist yet + debug!("Received new check entity, {}", new_entity.display()); + + // read the YAML configuration + + let config = match read_check_configuration(new_entity.clone()).await { + Ok(config) => config, + Err(e) => { + error!("Can't read check configuration: {}", e); + continue; + } + }; + + // check that a python check is available and prepare a CheckInstance + + let _checks = match prepare_checks(new_entity, config).await { + Ok(checks) => checks, + Err(e) => { + error!("Can't read check source code: {}", e); + continue; + } + }; + + // send all checks instances for scheduling to the scheduler + + // TODO(remy): send_to_scheduler(checks).await; } Some(deleted_entity) = deleted_entities.recv() => { debug!("Received deleted check entity {}", deleted_entity.display()); @@ -264,3 +320,62 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh info!("Check listener stopped."); } + +/// read_check_configuration receives a configuration file to open, reads it +/// and returns a CheckInstanceConfiguration if it's valid. +async fn read_check_configuration(relative_filepath: PathBuf) -> Result, Error> { + let file = std::fs::File::open(relative_filepath).expect("Could not open file."); + let mut checks_config = Vec::new(); + let read_yaml: YamlCheckConfiguration = match serde_yaml::from_reader(file) { + Ok(read) => read, + Err(e) => { + debug!("can't read configuration: {}", e); + return Err(Error::CantReadConfiguration{source: e}); + }, + }; + + for instance in read_yaml.instance.into_iter() { + checks_config.push(CheckInstanceConfiguration{ + min_collection_interval_ms: instance.min_collection_interval, + }); + } + + return Ok(checks_config); +} + +/// prepare_checks prepares the CheckInstances to be ready to be scheduled +/// in the scheduler. +async fn prepare_checks(relative_filepath: PathBuf, config: Vec) -> Result, Error> { + debug!("reading the check implementation: {}", relative_filepath.display()); + let mut checks = Vec::new(); + + let mut check_rel_filepath = relative_filepath.clone(); + check_rel_filepath.set_extension("py"); + +// let filename = check_rel_filepath.file_name().unwrap(); // TODO(remy): what about None here? + +// if !check_rel_filepath.pop() { +// return Err(Error::NoSourceAvailable{ reason: format!("Can't go to parent directory") }); +// } + +// check_rel_filepath.push(filename); + +// if !check_rel_filepath.exists() { + // check in a checks.d subdir +// check_rel_filepath.push("checks.d"); +// return Err(Error::NoSourceAvailable{ reason: format!("c") }); // TODO(remy): ship the rel filepath in the error +// } + + // TODO(remy): look for `check_name.d` directory instead. + + for instance_config in config.into_iter() { + debug!("created a check from {}, min_collection_interval: {}", check_rel_filepath.display(), instance_config.min_collection_interval_ms); + checks.push(CheckInstance{ + configuration: instance_config, + source_code_relative_filepath: relative_filepath.clone(), + }); + } + + return Ok(checks); +} + From ba6085aec095475a9d21f4c6b4a1ed9526b19dc7 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 14 May 2024 16:15:38 +0000 Subject: [PATCH 05/47] Adds concept of runnable check requests --- .../src/sources/checks/mod.rs | 242 +++++++++++------- .../src/sources/checks/runner.rs | 0 2 files changed, 148 insertions(+), 94 deletions(-) create mode 100644 lib/saluki-components/src/sources/checks/runner.rs diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 8743d601..d0a7d7fb 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -13,18 +13,27 @@ use saluki_core::{ use saluki_event::DataType; use serde::Deserialize; use snafu::Snafu; +use std::fmt::Display; use std::path::{Path, PathBuf}; use std::{collections::HashSet, io}; use tokio::{select, sync::mpsc}; -use tracing::{debug, info, error}; +use tracing::{debug, error, info}; + +mod runner; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] enum Error { #[snafu(display("Directory incorrect"))] - DirectoryIncorrect { source: io::Error }, - CantReadConfiguration { source: serde_yaml::Error }, - NoSourceAvailable { reason: String }, + DirectoryIncorrect { + source: io::Error, + }, + CantReadConfiguration { + source: serde_yaml::Error, + }, + NoSourceAvailable { + reason: String, + }, } /// Checks source. @@ -41,53 +50,92 @@ fn default_check_config_dir() -> String { "./conf.d".to_string() } -fn default_check_implementation_dir() -> String { - "./checks.d".to_string() +#[derive(Debug, Clone)] +struct CheckRequest { + name: Option, + instances: Vec, + init_config: Option, + source: PathBuf, +} + +impl Display for CheckRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Source: {} # Instances: {}", + self.source.display(), + self.instances.len(), + )?; + if let Some(name) = &self.name { + write!(f, " Name: {}", name)? + } + + Ok(()) + } +} + +struct RunnableCheckRequest { + check_request: CheckRequest, + check_source_code: PathBuf, +} + +impl Display for RunnableCheckRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Request: {} CheckSource: {}", + self.check_request, + self.check_source_code.display() + ) + } } -struct DirCheckListener { +struct DirCheckRequestListener { base_path: PathBuf, - known_check_paths: Vec, + known_check_requests: Vec, + // These could all be oneshot channels I think // but maybe buffering is useful - new_path_tx: mpsc::Sender, - deleted_path_tx: mpsc::Sender, - new_path_rx: Option>, - deleted_path_rx: Option>, + new_path_tx: mpsc::Sender, + deleted_path_tx: mpsc::Sender, + new_path_rx: Option>, + deleted_path_rx: Option>, +} + +#[derive(Debug, Deserialize)] +struct YamlCheckConfiguration { + name: Option, + init_config: Option, + instances: Vec, +} + +#[derive(Debug, Deserialize)] +struct YamlCheckInstance { + #[serde(default = "default_min_collection_interval_ms")] + min_collection_interval: u32, + // todo support arbitrary other fields } struct DirCheckListenerContext { shutdown_handle: DynamicShutdownHandle, - listener: DirCheckListener, + listener: DirCheckRequestListener, } // checks configuration -#[derive(Debug, Deserialize)] -struct CheckInstance { - configuration: CheckInstanceConfiguration, - source_code_relative_filepath: PathBuf, -} -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] struct CheckInstanceConfiguration { + #[serde(default = "default_min_collection_interval_ms")] min_collection_interval_ms: u32, } -// YAML configuration - -#[derive(Debug, Deserialize)] -struct YamlCheckConfiguration { - instance: Vec -} - -#[derive(Debug, Deserialize)] -struct YamlCheckInstance { - min_collection_interval: u32, +fn default_min_collection_interval_ms() -> u32 { + 15_000 } -impl DirCheckListener { +impl DirCheckRequestListener { /// Constructs a new `Listener` that will monitor the specified path. - pub fn from_path>(path: P) -> Result { + pub fn from_path>(path: P) -> Result { let path_ref = path.as_ref(); if !path_ref.exists() { return Err(Error::DirectoryIncorrect { @@ -102,9 +150,9 @@ impl DirCheckListener { let (new_paths_tx, new_paths_rx) = mpsc::channel(100); let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); - Ok(DirCheckListener { + Ok(DirCheckRequestListener { base_path: path.as_ref().to_path_buf(), - known_check_paths: Vec::new(), + known_check_requests: Vec::new(), new_path_tx: new_paths_tx, deleted_path_tx: deleted_paths_tx, new_path_rx: Some(new_paths_rx), @@ -112,25 +160,41 @@ impl DirCheckListener { }) } - pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { + pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { + if self.new_path_rx.is_none() || self.deleted_path_rx.is_none() { + panic!("Invariant violated: subscribe called after consuming the receivers"); + } + (self.new_path_rx.take().unwrap(), self.deleted_path_rx.take().unwrap()) } - async fn update_check_entities(&mut self) { + async fn update_check_entities(&mut self) -> Result<(), Error> { let new_check_paths = self.get_check_entities().await; - let current: HashSet = HashSet::from_iter(self.known_check_paths.clone().into_iter()); + let current_paths = self.known_check_requests.iter().map(|e| e.source.clone()); + let current: HashSet = HashSet::from_iter(current_paths); let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); for entity in new.difference(¤t) { - self.known_check_paths.push(entity.clone()); // todo error handling - self.new_path_tx.send(entity.clone()).await; + let check_request = read_check_configuration(entity.clone()).await?; + self.known_check_requests.push(check_request.clone()); + // todo error handling + self.new_path_tx.send(check_request).await.expect("Could send"); } for entity in current.difference(&new) { - self.known_check_paths.retain(|e| e != entity); + // find the check request by path + let check_request = self + .known_check_requests + .iter() + .find(|e| e.source == *entity) + .expect("couldn't find to-be-removed check") + .clone(); + self.known_check_requests.retain(|e| e.source != *entity); // todo error handling - self.deleted_path_tx.send(entity.clone()).await; + self.deleted_path_tx.send(check_request).await.expect("Could send"); } + + Ok(()) } /// Retrieves all check entities from the base path that match the required check formats. @@ -190,10 +254,10 @@ impl ChecksConfiguration { Ok(config.as_typed()?) } - async fn build_listeners(&self) -> Result, Error> { + async fn build_listeners(&self) -> Result, Error> { let mut listeners = Vec::new(); - let listener = DirCheckListener::from_path(&self.check_config_dir)?; + let listener = DirCheckRequestListener::from_path(&self.check_config_dir)?; listeners.push(listener); @@ -217,7 +281,7 @@ impl SourceBuilder for ChecksConfiguration { } pub struct Checks { - listeners: Vec, + listeners: Vec, } #[async_trait] @@ -272,7 +336,12 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh break; } _ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => { - listener.update_check_entities().await; + match listener.update_check_entities().await { + Ok(_) => {} + Err(e) => { + error!("Error updating check entities: {}", e); + } + } } Some(new_entity) = new_entities.recv() => { // TODO - try to start running this check @@ -281,35 +350,24 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh // in 'checks.d' as well. // This is almost identical to the 'DirCheckListener' in that it just looks // at a directory and finds anything ending in '.py' - // This component doesn't exist yet - debug!("Received new check entity, {}", new_entity.display()); - - // read the YAML configuration - - let config = match read_check_configuration(new_entity.clone()).await { - Ok(config) => config, - Err(e) => { - error!("Can't read check configuration: {}", e); - continue; - } - }; + debug!("Received new check entity, {}", new_entity); // Use the Display trait to format the new_entity variable // check that a python check is available and prepare a CheckInstance - let _checks = match prepare_checks(new_entity, config).await { + let check_request = match prepare_checks(new_entity).await { Ok(checks) => checks, Err(e) => { error!("Can't read check source code: {}", e); continue; } }; - + info!("Running a check request: {check_request}") // send all checks instances for scheduling to the scheduler // TODO(remy): send_to_scheduler(checks).await; } Some(deleted_entity) = deleted_entities.recv() => { - debug!("Received deleted check entity {}", deleted_entity.display()); + debug!("Received deleted check entity {}", deleted_entity); // TODO - try to stop running this check // (low priority) } @@ -321,61 +379,57 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh info!("Check listener stopped."); } -/// read_check_configuration receives a configuration file to open, reads it -/// and returns a CheckInstanceConfiguration if it's valid. -async fn read_check_configuration(relative_filepath: PathBuf) -> Result, Error> { - let file = std::fs::File::open(relative_filepath).expect("Could not open file."); +async fn read_check_configuration(relative_filepath: PathBuf) -> Result { + let file = std::fs::File::open(&relative_filepath).expect("Could not open file."); let mut checks_config = Vec::new(); let read_yaml: YamlCheckConfiguration = match serde_yaml::from_reader(file) { Ok(read) => read, Err(e) => { - debug!("can't read configuration: {}", e); - return Err(Error::CantReadConfiguration{source: e}); - }, + debug!("can't read configuration at {}: {}", relative_filepath.display(), e); + return Err(Error::CantReadConfiguration { source: e }); + } }; - for instance in read_yaml.instance.into_iter() { - checks_config.push(CheckInstanceConfiguration{ + for instance in read_yaml.instances.into_iter() { + checks_config.push(CheckInstanceConfiguration { min_collection_interval_ms: instance.min_collection_interval, }); } - return Ok(checks_config); + Ok(CheckRequest { + name: read_yaml.name, + instances: checks_config, + init_config: None, + source: relative_filepath, + }) } -/// prepare_checks prepares the CheckInstances to be ready to be scheduled -/// in the scheduler. -async fn prepare_checks(relative_filepath: PathBuf, config: Vec) -> Result, Error> { +/// Looks for a check implementation for the requested check +/// Currently only supports a sibling *.py file with the same name +async fn prepare_checks(check_request: CheckRequest) -> Result { + let relative_filepath = &check_request.source; debug!("reading the check implementation: {}", relative_filepath.display()); - let mut checks = Vec::new(); let mut check_rel_filepath = relative_filepath.clone(); check_rel_filepath.set_extension("py"); -// let filename = check_rel_filepath.file_name().unwrap(); // TODO(remy): what about None here? + // let filename = check_rel_filepath.file_name().unwrap(); // TODO(remy): what about None here? -// if !check_rel_filepath.pop() { -// return Err(Error::NoSourceAvailable{ reason: format!("Can't go to parent directory") }); -// } + // if !check_rel_filepath.pop() { + // return Err(Error::NoSourceAvailable{ reason: format!("Can't go to parent directory") }); + // } -// check_rel_filepath.push(filename); + // check_rel_filepath.push(filename); -// if !check_rel_filepath.exists() { - // check in a checks.d subdir -// check_rel_filepath.push("checks.d"); -// return Err(Error::NoSourceAvailable{ reason: format!("c") }); // TODO(remy): ship the rel filepath in the error -// } + // if !check_rel_filepath.exists() { + // check in a checks.d subdir + // check_rel_filepath.push("checks.d"); + // return Err(Error::NoSourceAvailable{ reason: format!("c") }); // TODO(remy): ship the rel filepath in the error + // } // TODO(remy): look for `check_name.d` directory instead. - - for instance_config in config.into_iter() { - debug!("created a check from {}, min_collection_interval: {}", check_rel_filepath.display(), instance_config.min_collection_interval_ms); - checks.push(CheckInstance{ - configuration: instance_config, - source_code_relative_filepath: relative_filepath.clone(), - }); - } - - return Ok(checks); + Ok(RunnableCheckRequest { + check_request, + check_source_code: check_rel_filepath, + }) } - diff --git a/lib/saluki-components/src/sources/checks/runner.rs b/lib/saluki-components/src/sources/checks/runner.rs new file mode 100644 index 00000000..e69de29b From b54d889775cb91ced2b1024364f972d757493b52 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 14 May 2024 18:46:07 +0000 Subject: [PATCH 06/47] Adds a check runner --- .../src/sources/checks/mod.rs | 40 ++++++++++++- .../src/sources/checks/runner.rs | 56 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index d0a7d7fb..3b0968ba 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -19,6 +19,8 @@ use std::{collections::HashSet, io}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; +use self::runner::CheckRunner; + mod runner; #[derive(Debug, Snafu)] @@ -119,6 +121,8 @@ struct YamlCheckInstance { struct DirCheckListenerContext { shutdown_handle: DynamicShutdownHandle, listener: DirCheckRequestListener, + check_run_tx: mpsc::Sender, + check_stop_tx: mpsc::Sender, } // checks configuration @@ -268,9 +272,17 @@ impl ChecksConfiguration { #[async_trait] impl SourceBuilder for ChecksConfiguration { async fn build(&self) -> Result, Box> { + let (check_run_tx, check_run_rx) = mpsc::channel(100); + let (check_stop_tx, check_stop_rx) = mpsc::channel(100); + let runner = CheckRunner::new(check_run_rx, check_stop_rx)?; let listeners = self.build_listeners().await?; - Ok(Box::new(Checks { listeners })) + Ok(Box::new(Checks { + listeners, + runner, + check_run_tx, + check_stop_tx, + })) } fn outputs(&self) -> &[OutputDefinition] { @@ -282,6 +294,9 @@ impl SourceBuilder for ChecksConfiguration { pub struct Checks { listeners: Vec, + runner: CheckRunner, + check_run_tx: mpsc::Sender, + check_stop_tx: mpsc::Sender, } #[async_trait] @@ -293,16 +308,31 @@ impl Source for Checks { let mut listener_shutdown_coordinator = DynamicShutdownCoordinator::default(); + let Checks { + listeners, + runner, + check_run_tx, + check_stop_tx, + } = *self; + // For each listener, spawn a dedicated task to run it. - for listener in self.listeners { + for listener in listeners { let listener_context = DirCheckListenerContext { shutdown_handle: listener_shutdown_coordinator.register(), listener, + check_run_tx: check_run_tx.clone(), + check_stop_tx: check_stop_tx.clone(), }; tokio::spawn(process_listener(context.clone(), listener_context)); } + let runner_shutdown_handle = listener_shutdown_coordinator.register(); + tokio::spawn(async move { + // will never return + runner.run(runner_shutdown_handle).await; + }); + info!("Check source started."); // Wait for the global shutdown signal, then notify listeners to shutdown. @@ -321,6 +351,8 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh let DirCheckListenerContext { shutdown_handle, mut listener, + check_run_tx, + check_stop_tx, } = listener_context; tokio::pin!(shutdown_handle); @@ -361,15 +393,17 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh continue; } }; - info!("Running a check request: {check_request}") + info!("Running a check request: {check_request}"); // send all checks instances for scheduling to the scheduler // TODO(remy): send_to_scheduler(checks).await; + check_run_tx.send(check_request).await.expect("Could send"); } Some(deleted_entity) = deleted_entities.recv() => { debug!("Received deleted check entity {}", deleted_entity); // TODO - try to stop running this check // (low priority) + check_stop_tx.send(deleted_entity).await.expect("Could send"); } } } diff --git a/lib/saluki-components/src/sources/checks/runner.rs b/lib/saluki-components/src/sources/checks/runner.rs index e69de29b..f813b3be 100644 --- a/lib/saluki-components/src/sources/checks/runner.rs +++ b/lib/saluki-components/src/sources/checks/runner.rs @@ -0,0 +1,56 @@ +use saluki_core::topology::shutdown::DynamicShutdownHandle; +use snafu::Snafu; +use tokio::{sync::mpsc, task::JoinHandle}; +use tracing::info; + +use super::{CheckRequest, RunnableCheckRequest}; + +#[derive(Debug, Snafu)] +#[snafu(context(suffix(false)))] +pub enum Error {} + +pub struct CheckRunner { + new_checks_rx: mpsc::Receiver, + deleted_checks_rx: mpsc::Receiver, +} + +impl CheckRunner { + pub fn new( + new_checks_rx: mpsc::Receiver, deleted_checks_rx: mpsc::Receiver, + ) -> Result { + Ok(CheckRunner { + new_checks_rx, + deleted_checks_rx, + }) + } + pub async fn run(mut self, shutdown_handle: DynamicShutdownHandle) -> JoinHandle<()> { + tokio::spawn(async move { + tokio::pin!(shutdown_handle); + + loop { + tokio::select! { + _ = &mut shutdown_handle => { + info!("Got shutdown signal, shutting down now"); + break; + } + Some(check) = self.new_checks_rx.recv() => { + self.add_check(check).await; + } + Some(check) = self.deleted_checks_rx.recv() => { + self.delete_check(check).await; + } + else => { + break; // when channels are closed, exit + } + } + } + }) + } + + async fn add_check(&self, check: RunnableCheckRequest) { + info!("Adding check request {check}"); + } + async fn delete_check(&self, check: CheckRequest) { + info!("Deleting check request {check}"); + } +} From fd27b4fd4499bb99bfd598ca5adb3aab7004b045 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 14 May 2024 19:46:58 +0000 Subject: [PATCH 07/47] 'runs' checks on the specified interval --- .../src/sources/checks/mod.rs | 3 +-- .../src/sources/checks/runner.rs | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 3b0968ba..97fee70c 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -127,9 +127,8 @@ struct DirCheckListenerContext { // checks configuration -#[derive(Debug, Deserialize, Clone)] +#[derive(Debug, Clone, Eq, Hash, PartialEq)] struct CheckInstanceConfiguration { - #[serde(default = "default_min_collection_interval_ms")] min_collection_interval_ms: u32, } diff --git a/lib/saluki-components/src/sources/checks/runner.rs b/lib/saluki-components/src/sources/checks/runner.rs index f813b3be..6f4261bb 100644 --- a/lib/saluki-components/src/sources/checks/runner.rs +++ b/lib/saluki-components/src/sources/checks/runner.rs @@ -1,9 +1,11 @@ +use std::{collections::HashMap, path::PathBuf, time::Duration}; + use saluki_core::topology::shutdown::DynamicShutdownHandle; use snafu::Snafu; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::info; -use super::{CheckRequest, RunnableCheckRequest}; +use super::{CheckInstanceConfiguration, CheckRequest, RunnableCheckRequest}; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -12,6 +14,8 @@ pub enum Error {} pub struct CheckRunner { new_checks_rx: mpsc::Receiver, deleted_checks_rx: mpsc::Receiver, + // key here is the configuration that spawned this check + running: HashMap>>, } impl CheckRunner { @@ -21,6 +25,7 @@ impl CheckRunner { Ok(CheckRunner { new_checks_rx, deleted_checks_rx, + running: HashMap::new(), }) } pub async fn run(mut self, shutdown_handle: DynamicShutdownHandle) -> JoinHandle<()> { @@ -47,8 +52,24 @@ impl CheckRunner { }) } - async fn add_check(&self, check: RunnableCheckRequest) { + async fn add_check(&mut self, check: RunnableCheckRequest) { info!("Adding check request {check}"); + let py_source = tokio::fs::read_to_string(&check.check_source_code).await.unwrap(); + let running = self.running.entry(check.check_source_code).or_default(); + for instance in check.check_request.instances { + let py_source = py_source.clone(); + let idx = running.len(); + let handle = tokio::spawn(async move { + let mut interval = + tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); + loop { + interval.tick().await; + // run check + info!("Running check instance {idx} on {}", py_source); + } + }); + running.push(handle); + } } async fn delete_check(&self, check: CheckRequest) { info!("Deleting check request {check}"); From 9524dec31a3935fd8b4a445b7f534b9784a5246d Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 14 May 2024 21:51:10 +0000 Subject: [PATCH 08/47] Adds example check --- conf.d/simple.py | 7 +++++++ conf.d/simple.yaml | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 conf.d/simple.py create mode 100644 conf.d/simple.yaml diff --git a/conf.d/simple.py b/conf.d/simple.py new file mode 100644 index 00000000..c952cd6f --- /dev/null +++ b/conf.d/simple.py @@ -0,0 +1,7 @@ +import sys + +from datadog_checks.checks import AgentCheck + +class ChurnCheck(AgentCheck): + def check(self, instance): + self.gauge('computed_value', 42, tags=['hello:world']) diff --git a/conf.d/simple.yaml b/conf.d/simple.yaml new file mode 100644 index 00000000..5f502a55 --- /dev/null +++ b/conf.d/simple.yaml @@ -0,0 +1,7 @@ +instances: + - argument: something + - argument: something2 + - argument: something3 + - argument: something4 + - argument: something5 + - argument: something7 From 8eb1de111e2699d6393de076b3e536945d4d31d3 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 15 May 2024 13:31:43 +0000 Subject: [PATCH 09/47] small refactor to warmup for the day --- .../src/sources/checks/listener.rs | 134 +++++++++ .../src/sources/checks/mod.rs | 254 +++++------------- 2 files changed, 201 insertions(+), 187 deletions(-) create mode 100644 lib/saluki-components/src/sources/checks/listener.rs diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs new file mode 100644 index 00000000..f9b3ca54 --- /dev/null +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -0,0 +1,134 @@ +use super::*; + +pub struct DirCheckRequestListener { + pub base_path: PathBuf, + pub known_check_requests: Vec, + + // These could all be oneshot channels I think + // but maybe buffering is useful + pub new_path_tx: mpsc::Sender, + pub deleted_path_tx: mpsc::Sender, + pub new_path_rx: Option>, + pub deleted_path_rx: Option>, +} + +impl DirCheckRequestListener { + /// Constructs a new `Listener` that will monitor the specified path. + pub fn from_path>(path: P) -> Result { + let path_ref = path.as_ref(); + if !path_ref.exists() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), + }); + } + if !path_ref.is_dir() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new(io::ErrorKind::NotFound, "Path is not a directory"), + }); + } + + let (new_paths_tx, new_paths_rx) = mpsc::channel(100); + let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); + Ok(DirCheckRequestListener { + base_path: path.as_ref().to_path_buf(), + known_check_requests: Vec::new(), + new_path_tx: new_paths_tx, + deleted_path_tx: deleted_paths_tx, + new_path_rx: Some(new_paths_rx), + deleted_path_rx: Some(deleted_paths_rx), + }) + } + + pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { + if self.new_path_rx.is_none() || self.deleted_path_rx.is_none() { + panic!("Invariant violated: subscribe called after consuming the receivers"); + } + + (self.new_path_rx.take().unwrap(), self.deleted_path_rx.take().unwrap()) + } + + pub async fn update_check_entities(&mut self) -> Result<(), Error> { + let new_check_paths = self.get_check_entities().await; + let current_paths = self.known_check_requests.iter().map(|e| e.source.clone()); + let current: HashSet = HashSet::from_iter(current_paths); + let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); + + for entity in new.difference(¤t) { + let check_request = entity.to_check_request()?; + self.known_check_requests.push(check_request.clone()); + // todo error handling + self.new_path_tx.send(check_request).await.expect("Could send"); + } + for entity in current.difference(&new) { + // find the check request by path + let check_request = self + .known_check_requests + .iter() + .find(|e| e.source == *entity) + .expect("couldn't find to-be-removed check") + .clone(); + self.known_check_requests.retain(|e| e.source != *entity); + // todo error handling + self.deleted_path_tx.send(check_request).await.expect("Could send"); + } + + Ok(()) + } + + /// Retrieves all check entities from the base path that match the required check formats. + pub async fn get_check_entities(&self) -> Vec { + let entries = WalkDir::new(&self.base_path).filter(|entry| async move { + if let Some(true) = entry.path().file_name().map(|f| f.to_string_lossy().starts_with('.')) { + return Filtering::IgnoreDir; + } + if is_check_entity(&entry).await { + Filtering::Continue + } else { + Filtering::Ignore + } + }); + + entries + .filter_map(|e| async move { + match e { + Ok(entry) => Some(CheckSource::Yaml(entry.path().to_path_buf())), + Err(e) => { + eprintln!("Error traversing files: {}", e); + None + } + } + }) + .collect() + .await + } +} + +/// Determines if a directory entry is a valid check entity based on defined patterns. +async fn is_check_entity(entry: &DirEntry) -> bool { + let path = entry.path(); + let file_type = entry.file_type().await.expect("Couldn't get file type"); + if file_type.is_file() { + // Matches `./mycheck.yaml` + return path.extension().unwrap_or_default() == "yaml"; + } + + if file_type.is_dir() { + // Matches `./mycheck.d/conf.yaml` + let conf_path = path.join("conf.yaml"); + return conf_path.exists() + && path + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or("") + .ends_with("check.d"); + } + false +} + +pub struct DirCheckListenerContext { + pub shutdown_handle: DynamicShutdownHandle, + pub listener: DirCheckRequestListener, + pub check_run_tx: mpsc::Sender, + pub check_stop_tx: mpsc::Sender, +} diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 97fee70c..ec2b20fd 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -21,6 +21,7 @@ use tracing::{debug, error, info}; use self::runner::CheckRunner; +mod listener; mod runner; #[derive(Debug, Snafu)] @@ -57,17 +58,12 @@ struct CheckRequest { name: Option, instances: Vec, init_config: Option, - source: PathBuf, + source: CheckSource, } impl Display for CheckRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Source: {} # Instances: {}", - self.source.display(), - self.instances.len(), - )?; + write!(f, "Source: {} # Instances: {}", self.source, self.instances.len(),)?; if let Some(name) = &self.name { write!(f, " Name: {}", name)? } @@ -76,6 +72,18 @@ impl Display for CheckRequest { } } +impl CheckRequest { + fn to_runnable_request(&self) -> Result { + let check_source_code = match &self.source { + CheckSource::Yaml(path) => find_sibling_py_file(path)?, + }; + Ok(RunnableCheckRequest { + check_request: self.clone(), + check_source_code, + }) + } +} + struct RunnableCheckRequest { check_request: CheckRequest, check_source_code: PathBuf, @@ -92,18 +100,6 @@ impl Display for RunnableCheckRequest { } } -struct DirCheckRequestListener { - base_path: PathBuf, - known_check_requests: Vec, - - // These could all be oneshot channels I think - // but maybe buffering is useful - new_path_tx: mpsc::Sender, - deleted_path_tx: mpsc::Sender, - new_path_rx: Option>, - deleted_path_rx: Option>, -} - #[derive(Debug, Deserialize)] struct YamlCheckConfiguration { name: Option, @@ -118,13 +114,6 @@ struct YamlCheckInstance { // todo support arbitrary other fields } -struct DirCheckListenerContext { - shutdown_handle: DynamicShutdownHandle, - listener: DirCheckRequestListener, - check_run_tx: mpsc::Sender, - check_stop_tx: mpsc::Sender, -} - // checks configuration #[derive(Debug, Clone, Eq, Hash, PartialEq)] @@ -136,119 +125,49 @@ fn default_min_collection_interval_ms() -> u32 { 15_000 } -impl DirCheckRequestListener { - /// Constructs a new `Listener` that will monitor the specified path. - pub fn from_path>(path: P) -> Result { - let path_ref = path.as_ref(); - if !path_ref.exists() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), - }); - } - if !path_ref.is_dir() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new(io::ErrorKind::NotFound, "Path is not a directory"), - }); - } - - let (new_paths_tx, new_paths_rx) = mpsc::channel(100); - let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); - Ok(DirCheckRequestListener { - base_path: path.as_ref().to_path_buf(), - known_check_requests: Vec::new(), - new_path_tx: new_paths_tx, - deleted_path_tx: deleted_paths_tx, - new_path_rx: Some(new_paths_rx), - deleted_path_rx: Some(deleted_paths_rx), - }) - } - - pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { - if self.new_path_rx.is_none() || self.deleted_path_rx.is_none() { - panic!("Invariant violated: subscribe called after consuming the receivers"); - } - - (self.new_path_rx.take().unwrap(), self.deleted_path_rx.take().unwrap()) - } - - async fn update_check_entities(&mut self) -> Result<(), Error> { - let new_check_paths = self.get_check_entities().await; - let current_paths = self.known_check_requests.iter().map(|e| e.source.clone()); - let current: HashSet = HashSet::from_iter(current_paths); - let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); - - for entity in new.difference(¤t) { - // todo error handling - let check_request = read_check_configuration(entity.clone()).await?; - self.known_check_requests.push(check_request.clone()); - // todo error handling - self.new_path_tx.send(check_request).await.expect("Could send"); - } - for entity in current.difference(&new) { - // find the check request by path - let check_request = self - .known_check_requests - .iter() - .find(|e| e.source == *entity) - .expect("couldn't find to-be-removed check") - .clone(); - self.known_check_requests.retain(|e| e.source != *entity); - // todo error handling - self.deleted_path_tx.send(check_request).await.expect("Could send"); - } - - Ok(()) - } - - /// Retrieves all check entities from the base path that match the required check formats. - pub async fn get_check_entities(&self) -> Vec { - let entries = WalkDir::new(&self.base_path).filter(|entry| async move { - if let Some(true) = entry.path().file_name().map(|f| f.to_string_lossy().starts_with('.')) { - return Filtering::IgnoreDir; - } - if is_check_entity(&entry).await { - Filtering::Continue - } else { - Filtering::Ignore - } - }); +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +enum CheckSource { + Yaml(PathBuf), +} - entries - .filter_map(|e| async move { - match e { - Ok(entry) => Some(entry.path().to_path_buf()), +impl CheckSource { + // todo refactor to async fs + fn to_check_request(&self) -> Result { + match self { + CheckSource::Yaml(path) => { + let file = std::fs::File::open(path).expect("No error"); + let mut checks_config = Vec::new(); + let read_yaml: YamlCheckConfiguration = match serde_yaml::from_reader(file) { + Ok(read) => read, Err(e) => { - eprintln!("Error traversing files: {}", e); - None + debug!("can't read configuration at {}: {}", path.display(), e); + return Err(Error::CantReadConfiguration { source: e }); } + }; + + for instance in read_yaml.instances.into_iter() { + checks_config.push(CheckInstanceConfiguration { + min_collection_interval_ms: instance.min_collection_interval, + }); } - }) - .collect() - .await - } -} -/// Determines if a directory entry is a valid check entity based on defined patterns. -async fn is_check_entity(entry: &DirEntry) -> bool { - let path = entry.path(); - let file_type = entry.file_type().await.expect("Couldn't get file type"); - if file_type.is_file() { - // Matches `./mycheck.yaml` - return path.extension().unwrap_or_default() == "yaml"; + Ok(CheckRequest { + name: read_yaml.name, + instances: checks_config, + init_config: read_yaml.init_config, + source: self.clone(), + }) + } + } } +} - if file_type.is_dir() { - // Matches `./mycheck.d/conf.yaml` - let conf_path = path.join("conf.yaml"); - return conf_path.exists() - && path - .file_name() - .unwrap_or_default() - .to_str() - .unwrap_or("") - .ends_with("check.d"); +impl Display for CheckSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CheckSource::Yaml(path) => write!(f, "{}", path.display()), + } } - false } impl ChecksConfiguration { @@ -257,10 +176,10 @@ impl ChecksConfiguration { Ok(config.as_typed()?) } - async fn build_listeners(&self) -> Result, Error> { + async fn build_listeners(&self) -> Result, Error> { let mut listeners = Vec::new(); - let listener = DirCheckRequestListener::from_path(&self.check_config_dir)?; + let listener = listener::DirCheckRequestListener::from_path(&self.check_config_dir)?; listeners.push(listener); @@ -292,7 +211,7 @@ impl SourceBuilder for ChecksConfiguration { } pub struct Checks { - listeners: Vec, + listeners: Vec, runner: CheckRunner, check_run_tx: mpsc::Sender, check_stop_tx: mpsc::Sender, @@ -316,7 +235,7 @@ impl Source for Checks { // For each listener, spawn a dedicated task to run it. for listener in listeners { - let listener_context = DirCheckListenerContext { + let listener_context = listener::DirCheckListenerContext { shutdown_handle: listener_shutdown_coordinator.register(), listener, check_run_tx: check_run_tx.clone(), @@ -346,8 +265,8 @@ impl Source for Checks { } } -async fn process_listener(source_context: SourceContext, listener_context: DirCheckListenerContext) { - let DirCheckListenerContext { +async fn process_listener(source_context: SourceContext, listener_context: listener::DirCheckListenerContext) { + let listener::DirCheckListenerContext { shutdown_handle, mut listener, check_run_tx, @@ -358,7 +277,6 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); info!("Check listener started."); - // every 1 sec check for new check entities let (mut new_entities, mut deleted_entities) = listener.subscribe(); loop { select! { @@ -375,23 +293,16 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh } } Some(new_entity) = new_entities.recv() => { - // TODO - try to start running this check - // So what component will be responsible for "running" the check? - // There needs to be a "check registry" component that finds the checks - // in 'checks.d' as well. - // This is almost identical to the 'DirCheckListener' in that it just looks - // at a directory and finds anything ending in '.py' - debug!("Received new check entity, {}", new_entity); // Use the Display trait to format the new_entity variable - - // check that a python check is available and prepare a CheckInstance - - let check_request = match prepare_checks(new_entity).await { - Ok(checks) => checks, + debug!("Received new check entity, {}", new_entity); + + let check_request = match new_entity.to_runnable_request() { + Ok(check_request) => check_request, Err(e) => { - error!("Can't read check source code: {}", e); + error!("Can't convert check source to runnable request: {}", e); continue; } }; + info!("Running a check request: {check_request}"); // send all checks instances for scheduling to the scheduler @@ -412,38 +323,10 @@ async fn process_listener(source_context: SourceContext, listener_context: DirCh info!("Check listener stopped."); } -async fn read_check_configuration(relative_filepath: PathBuf) -> Result { - let file = std::fs::File::open(&relative_filepath).expect("Could not open file."); - let mut checks_config = Vec::new(); - let read_yaml: YamlCheckConfiguration = match serde_yaml::from_reader(file) { - Ok(read) => read, - Err(e) => { - debug!("can't read configuration at {}: {}", relative_filepath.display(), e); - return Err(Error::CantReadConfiguration { source: e }); - } - }; - - for instance in read_yaml.instances.into_iter() { - checks_config.push(CheckInstanceConfiguration { - min_collection_interval_ms: instance.min_collection_interval, - }); - } - - Ok(CheckRequest { - name: read_yaml.name, - instances: checks_config, - init_config: None, - source: relative_filepath, - }) -} - -/// Looks for a check implementation for the requested check -/// Currently only supports a sibling *.py file with the same name -async fn prepare_checks(check_request: CheckRequest) -> Result { - let relative_filepath = &check_request.source; - debug!("reading the check implementation: {}", relative_filepath.display()); - - let mut check_rel_filepath = relative_filepath.clone(); +/// Given a yaml config, find the corresponding python source code +/// Currently only looks in the same directory, no support for `checks.d` or `mycheck.d` directories +fn find_sibling_py_file(check_yaml_path: &Path) -> Result { + let mut check_rel_filepath = check_yaml_path.to_path_buf(); check_rel_filepath.set_extension("py"); // let filename = check_rel_filepath.file_name().unwrap(); // TODO(remy): what about None here? @@ -461,8 +344,5 @@ async fn prepare_checks(check_request: CheckRequest) -> Result Date: Wed, 15 May 2024 18:57:29 +0000 Subject: [PATCH 10/47] Scheduler is partially implemented, python can run, but not run correctly --- Cargo.lock | 1470 ++++++++++++++++- lib/saluki-components/Cargo.toml | 2 + .../src/sources/checks/listener.rs | 2 - .../src/sources/checks/mod.rs | 143 +- .../src/sources/checks/runner.rs | 77 - .../src/sources/checks/scheduler.rs | 120 ++ 6 files changed, 1656 insertions(+), 158 deletions(-) delete mode 100644 lib/saluki-components/src/sources/checks/runner.rs create mode 100644 lib/saluki-components/src/sources/checks/scheduler.rs diff --git a/Cargo.lock b/Cargo.lock index fb20e5e3..3d16074d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "addr2line" version = "0.21.0" @@ -17,6 +23,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "agent-data-plane" version = "0.1.0" @@ -83,6 +95,12 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "async-channel" version = "2.3.0" @@ -150,7 +168,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -167,7 +185,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -180,6 +198,12 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "atomic" version = "0.6.0" @@ -195,6 +219,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -289,6 +324,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -320,7 +361,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.61", "which", ] @@ -343,7 +384,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9990737a6d5740ff51cdbbc0f0503015cb30c390f6623968281eb214a520cfc0" dependencies = [ "quote", - "syn", + "syn 2.0.61", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", ] [[package]] @@ -398,6 +448,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "caseless" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f" +dependencies = [ + "regex", + "unicode-normalization", +] + [[package]] name = "cc" version = "1.0.97" @@ -424,6 +484,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.38" @@ -450,6 +516,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "clipboard-win" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +dependencies = [ + "error-code", +] + [[package]] name = "cmake" version = "0.1.50" @@ -492,6 +567,12 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.4" @@ -541,6 +622,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -551,6 +638,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "datadog-protos" version = "0.1.0" @@ -586,6 +682,19 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -594,6 +703,19 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "dns-lookup" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.48.0", ] [[package]] @@ -602,6 +724,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "either" version = "1.11.0" @@ -614,6 +742,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "equivalent" version = "1.0.1" @@ -630,6 +764,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-code" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" + [[package]] name = "event-listener" version = "4.0.3" @@ -672,19 +812,36 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" + [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "figment" version = "0.10.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d032832d74006f99547004d49410a4b4218e4c33382d56ca3ff89df74f86b953" dependencies = [ - "atomic", + "atomic 0.6.0", "pear", "serde", "serde_json", @@ -805,7 +962,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -848,6 +1005,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -855,8 +1031,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -909,6 +1087,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "hashbrown" version = "0.12.3" @@ -936,12 +1120,33 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "home" version = "0.5.9" @@ -1212,6 +1417,18 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "is-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a85abdc13717906baccb5a1e435556ce0df215f242892f721dff62bf25288f" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1221,6 +1438,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1282,6 +1508,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "kube" version = "0.89.0" @@ -1347,6 +1582,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1359,6 +1600,36 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lexical-parse-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] + [[package]] name = "libc" version = "0.2.154" @@ -1381,18 +1652,122 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libsqlite3-sys" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "mac_address" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa12182b93606fff55b70a5cfe6130eaf7407c2ea4f2c2bcc8b113b67c9928f" +dependencies = [ + "nix 0.28.0", + "winapi", +] + +[[package]] +name = "malachite" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ad951c9d45bd4c77f4c843844870dddeb2e332864226f5a62277498e1627b8" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d073a3d1e4e037975af5ef176a2632672e25e8ddbe8e1811745c2e0726b6ad94" +dependencies = [ + "hashbrown 0.14.5", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-bigint" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17703a19c80bbdd0b7919f0f104f3b0597f7de4fc4e90a477c15366a5ba03faa" +dependencies = [ + "derive_more", + "malachite", + "num-integer", + "num-traits", + "paste", +] + +[[package]] +name = "malachite-nz" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2546fc6ae29728079e87a2a0f011509e6060713b65e62ee46ba5d413b495ebc7" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad0ac879ecbb7e1cc8060b48696eebed0b0de8d922db4f8b378dfbc1bad806a" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "match_cfg" version = "0.1.0" @@ -1408,6 +1783,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "matchit" version = "0.7.3" @@ -1424,6 +1805,16 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.2" @@ -1431,11 +1822,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] -name = "memory-accounting" -version = "0.1.0" +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ - "ordered-float 4.2.0", - "proptest", + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-accounting" +version = "0.1.0" +dependencies = [ + "ordered-float 4.2.0", + "proptest", ] [[package]] @@ -1500,6 +1909,15 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" +[[package]] +name = "mt19937" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ca7f22ed370d5991a9caec16a83187e865bc8a532f889670337d5a5689e3a1" +dependencies = [ + "rand_core", +] + [[package]] name = "multimap" version = "0.10.0" @@ -1534,6 +1952,40 @@ dependencies = [ "rand", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "noisy_float" version = "0.2.0" @@ -1603,10 +2055,31 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -1637,6 +2110,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "optional" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc" + [[package]] name = "ordered-float" version = "2.10.1" @@ -1661,12 +2140,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + [[package]] name = "paste" version = "1.0.15" @@ -1693,7 +2205,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1743,7 +2255,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1767,6 +2279,44 @@ dependencies = [ "indexmap 2.2.6", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -1784,7 +2334,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1810,6 +2360,23 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "pmutil" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "portable-atomic" version = "1.6.0" @@ -1835,7 +2402,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.61", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", ] [[package]] @@ -1855,7 +2431,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", "version_check", "yansi", ] @@ -1903,7 +2479,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 2.0.61", "tempfile", ] @@ -1917,7 +2493,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1981,6 +2557,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "puruspe" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a1eed715f625eaa95fba5e049dcf7bc06fa396d6d2e55015b3764e234dfd3f" + [[package]] name = "quanta" version = "0.12.3" @@ -2005,6 +2587,22 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -2069,6 +2667,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" version = "1.10.4" @@ -2113,6 +2720,28 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "result-like" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810" +dependencies = [ + "result-like-derive", +] + +[[package]] +name = "result-like-derive" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42" +dependencies = [ + "pmutil", + "proc-macro2", + "quote", + "syn 1.0.109", + "syn-ext", +] + [[package]] name = "ring" version = "0.17.8" @@ -2140,6 +2769,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.34" @@ -2209,12 +2847,380 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustpython-ast" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2c72612d246194023a643d342ac53e4118c300cbc3ada31ff1245e7636db860" +dependencies = [ + "is-macro", + "malachite-bigint", + "rustpython-literal", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-codegen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964cbc9d7870b4d092eecd66049ab0cd93fb5f3f40a408f78bf4415a5d25d3a3" +dependencies = [ + "ahash", + "bitflags 2.5.0", + "indexmap 2.2.6", + "itertools 0.11.0", + "log", + "num-complex", + "num-traits", + "rustpython-ast", + "rustpython-compiler-core", + "rustpython-parser-core", +] + +[[package]] +name = "rustpython-common" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5b1bae2d3bd9c18a74a835a496b2f7dedcea692e66d47ff56a1dd100ed35c9" +dependencies = [ + "ascii", + "bitflags 2.5.0", + "bstr", + "cfg-if", + "itertools 0.11.0", + "libc", + "lock_api", + "malachite-base", + "malachite-bigint", + "malachite-q", + "num-complex", + "num-traits", + "once_cell", + "radium", + "rand", + "rustpython-format", + "siphasher", + "volatile", + "widestring", +] + +[[package]] +name = "rustpython-compiler" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4849236178a0296808c30fca6f7bf8030b57a9b6e85223129b23ae5bfb9fa3" +dependencies = [ + "rustpython-codegen", + "rustpython-compiler-core", + "rustpython-parser", +] + +[[package]] +name = "rustpython-compiler-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf4f4dabec9dec0250c2544bab9229428c7a6af04dba34508476e975fbbc3a1" +dependencies = [ + "bitflags 2.5.0", + "itertools 0.11.0", + "lz4_flex", + "malachite-bigint", + "num-complex", + "rustpython-parser-core", +] + +[[package]] +name = "rustpython-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcac29b3573c59fa22e2d7457b12c07e502b7dfbd38ce62bdd23a25f3d8a8eb" +dependencies = [ + "rustpython-compiler", + "rustpython-derive-impl", + "syn 1.0.109", +] + +[[package]] +name = "rustpython-derive-impl" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe0c74ae690e16c0d08a07236d5736989a3c9a5fc793e04f5ee4b8fbd3c60a9" +dependencies = [ + "itertools 0.11.0", + "maplit", + "once_cell", + "proc-macro2", + "quote", + "rustpython-compiler-core", + "rustpython-doc", + "rustpython-parser-core", + "syn 1.0.109", + "syn-ext", + "textwrap", +] + +[[package]] +name = "rustpython-doc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885d19895d9d29656a8a2b33e967a482b92f3d891b4fd923e40849714051bcd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "rustpython-format" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba07bbc520a3884d6c7c91bdcc2d49b6645b8d8ea6d13c4bf8300a7ddea3ad72" +dependencies = [ + "bitflags 2.5.0", + "itertools 0.11.0", + "malachite-bigint", + "num-traits", + "rustpython-literal", +] + +[[package]] +name = "rustpython-literal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443cbcbd1a87a27be77e24b16654716900dc9151db57c02380a2b480ecce72c0" +dependencies = [ + "hexf-parse", + "is-macro", + "lexical-parse-float", + "num-traits", + "unic-ucd-category", +] + +[[package]] +name = "rustpython-parser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e73421499eff08722b979d8494c3f1bc035908931a28607a9f17be6143ac11" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.11.0", + "lalrpop-util", + "log", + "malachite-bigint", + "num-traits", + "phf", + "phf_codegen", + "rustc-hash", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47f9465e822d45e05304c08f168dd1ffea05d8d95b7a4e18651ef1acb4df097f" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e43398f39b5e4ce129cb95a8d7b87c6378b056d66c978da86ea2296d861c8f" +dependencies = [ + "memchr", + "once_cell", +] + +[[package]] +name = "rustpython-sre_engine" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf3bb085265414a74cc9c6e90316ea6e423f582c3ca86c34430a39b9f150d93" +dependencies = [ + "bitflags 2.5.0", + "num_enum", + "optional", +] + +[[package]] +name = "rustpython-stdlib" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c175766165785ef4e528e7ee698384b913f20257eed9cd112f0f785ce03578" +dependencies = [ + "adler32", + "ahash", + "ascii", + "base64 0.13.1", + "blake2", + "cfg-if", + "crc32fast", + "crossbeam-utils", + "csv-core", + "digest", + "dns-lookup", + "dyn-clone", + "flate2", + "gethostname", + "hex", + "itertools 0.11.0", + "libc", + "libsqlite3-sys", + "mac_address", + "malachite-bigint", + "md-5", + "memchr", + "memmap2", + "mt19937", + "nix 0.27.1", + "num-complex", + "num-integer", + "num-traits", + "num_enum", + "once_cell", + "page_size", + "parking_lot", + "paste", + "puruspe", + "rand", + "rand_core", + "rustpython-common", + "rustpython-derive", + "rustpython-vm", + "schannel", + "sha-1", + "sha2", + "sha3", + "socket2", + "system-configuration", + "termios", + "ucd", + "unic-char-property", + "unic-normal", + "unic-ucd-age", + "unic-ucd-bidi", + "unic-ucd-category", + "unic-ucd-ident", + "unicode-casing", + "unicode_names2", + "uuid", + "widestring", + "winapi", + "windows-sys 0.52.0", + "xml-rs", +] + +[[package]] +name = "rustpython-vm" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d304aecce4f914def54abf331e458ee99f611b862580acb0c448e67a8237d28d" +dependencies = [ + "ahash", + "ascii", + "atty", + "bitflags 2.5.0", + "bstr", + "caseless", + "cfg-if", + "chrono", + "crossbeam-utils", + "exitcode", + "getrandom", + "glob", + "half", + "hex", + "indexmap 2.2.6", + "is-macro", + "itertools 0.11.0", + "libc", + "log", + "malachite-bigint", + "memchr", + "memoffset", + "nix 0.27.1", + "num-complex", + "num-integer", + "num-traits", + "num_cpus", + "num_enum", + "once_cell", + "optional", + "parking_lot", + "paste", + "rand", + "result-like", + "rustc_version", + "rustpython-ast", + "rustpython-codegen", + "rustpython-common", + "rustpython-compiler", + "rustpython-compiler-core", + "rustpython-derive", + "rustpython-format", + "rustpython-literal", + "rustpython-parser", + "rustpython-parser-core", + "rustpython-sre_engine", + "rustyline", + "schannel", + "static_assertions", + "strum", + "strum_macros", + "thiserror", + "thread_local", + "timsort", + "uname", + "unic-ucd-bidi", + "unic-ucd-category", + "unic-ucd-ident", + "unicode-casing", + "unicode_names2", + "wasm-bindgen", + "which", + "widestring", + "windows", + "windows-sys 0.52.0", + "winreg", +] + [[package]] name = "rustversion" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +[[package]] +name = "rustyline" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix 0.28.0", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -2263,6 +3269,8 @@ dependencies = [ "protobuf", "quanta", "rustls", + "rustpython-stdlib", + "rustpython-vm", "saluki-config", "saluki-core", "saluki-env", @@ -2432,6 +3440,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "secrecy" version = "0.8.0" @@ -2465,6 +3479,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.201" @@ -2492,7 +3512,7 @@ checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2519,6 +3539,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -2530,6 +3561,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2574,6 +3615,12 @@ dependencies = [ "similar", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -2607,7 +3654,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2626,12 +3673,48 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.61" @@ -2643,12 +3726,42 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-ext" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6" +dependencies = [ + "syn 1.0.109", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -2661,6 +3774,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "textwrap" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" + [[package]] name = "thiserror" version = "1.0.60" @@ -2678,7 +3806,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2724,6 +3852,21 @@ dependencies = [ "time-core", ] +[[package]] +name = "timsort" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639ce8ef6d2ba56be0383a94dd13b92138d58de44c62618303bb798fa92bdc00" + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2775,7 +3918,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2814,6 +3957,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.11.0" @@ -2851,7 +4011,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2925,7 +4085,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2973,18 +4133,43 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4fa6e588762366f1eb4991ce59ad1b93651d0b769dfb4e4d1c5c4b943d1159" + [[package]] name = "ucd-trie" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unarray" version = "0.1.4" @@ -3000,12 +4185,134 @@ dependencies = [ "version_check", ] +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-normal" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09d64d33589a94628bc2aeb037f35c2e25f3f049c7348b5aa5580b48e6bba62" +dependencies = [ + "unic-ucd-normal", +] + +[[package]] +name = "unic-ucd-age" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8cfdfe71af46b871dc6af2c24fcd360e2f3392ee4c5111877f2947f311671c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-category" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" +dependencies = [ + "matches", + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-hangul" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1dc690e19010e1523edb9713224cba5ef55b54894fe33424439ec9a40c0054" +dependencies = [ + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-normal" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86aed873b8202d22b13859dda5fe7c001d271412c31d411fd9b827e030569410" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-hangul", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-casing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -3027,6 +4334,34 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "unicode_names2" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addeebf294df7922a1164f729fb27ebbbcea99cc32b3bf08afab62757f707677" +dependencies = [ + "phf", + "unicode_names2_generator", +] + +[[package]] +name = "unicode_names2_generator" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f444b8bba042fe3c1251ffaca35c603f2dc2ccc08d595c65a8c4f76f3e8426c0" +dependencies = [ + "getopts", + "log", + "phf_codegen", + "rand", +] + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3056,18 +4391,59 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "atomic 0.5.3", + "getrandom", + "rand", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "volatile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e76fae08f03f96e166d2dfda232190638c10e0383841252416f9cfe2ae60e6" + [[package]] name = "want" version = "0.3.1" @@ -3104,7 +4480,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -3126,7 +4502,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3159,6 +4535,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -3181,6 +4563,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3329,6 +4721,30 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + [[package]] name = "yansi" version = "1.0.1" @@ -3352,7 +4768,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 34de9c9e..d9ee8d70 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -48,3 +48,5 @@ tower = { workspace = true } tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" +rustpython-vm = "0.3.1" +rustpython-stdlib = "0.3.1" diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index f9b3ca54..88a7cb88 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -129,6 +129,4 @@ async fn is_check_entity(entry: &DirEntry) -> bool { pub struct DirCheckListenerContext { pub shutdown_handle: DynamicShutdownHandle, pub listener: DirCheckRequestListener, - pub check_run_tx: mpsc::Sender, - pub check_stop_tx: mpsc::Sender, } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index ec2b20fd..bcf74098 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,28 +1,33 @@ use async_trait::async_trait; use async_walkdir::{DirEntry, Filtering, WalkDir}; use futures::StreamExt as _; +use rustpython_vm::Interpreter; use saluki_config::GenericConfiguration; use saluki_core::{ components::{Source, SourceBuilder, SourceContext}, prelude::ErasedError, topology::{ - shutdown::{DynamicShutdownCoordinator, DynamicShutdownHandle}, + shutdown::{ + ComponentShutdownCoordinator, ComponentShutdownHandle, DynamicShutdownCoordinator, DynamicShutdownHandle, + }, OutputDefinition, }, }; use saluki_event::DataType; use serde::Deserialize; use snafu::Snafu; -use std::fmt::Display; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + thread, +}; use std::{collections::HashSet, io}; +use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; -use self::runner::CheckRunner; - mod listener; -mod runner; +mod scheduler; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -37,8 +42,21 @@ enum Error { NoSourceAvailable { reason: String, }, + Python { + reason: String, + }, } +impl From> for Error { + fn from(err: rustpython_vm::PyRef) -> Self { + Error::Python { + reason: format!("Error: {:?}", err), + } + } +} + +// implement Error#from> + /// Checks source. /// /// Scans a directory for check configurations and emits them as things to run. @@ -190,16 +208,15 @@ impl ChecksConfiguration { #[async_trait] impl SourceBuilder for ChecksConfiguration { async fn build(&self) -> Result, Box> { - let (check_run_tx, check_run_rx) = mpsc::channel(100); - let (check_stop_tx, check_stop_rx) = mpsc::channel(100); - let runner = CheckRunner::new(check_run_rx, check_stop_rx)?; + //let (check_run_tx, check_run_rx) = mpsc::channel(100); + //let (check_stop_tx, check_stop_rx) = mpsc::channel(100); + //let runner = CheckRunner::new(check_run_rx, check_stop_rx)?; let listeners = self.build_listeners().await?; Ok(Box::new(Checks { listeners, - runner, - check_run_tx, - check_stop_tx, + //check_run_tx, + //check_stop_tx, })) } @@ -212,52 +229,49 @@ impl SourceBuilder for ChecksConfiguration { pub struct Checks { listeners: Vec, - runner: CheckRunner, - check_run_tx: mpsc::Sender, - check_stop_tx: mpsc::Sender, } -#[async_trait] -impl Source for Checks { - async fn run(mut self: Box, mut context: SourceContext) -> Result<(), ()> { - let global_shutdown = context - .take_shutdown_handle() - .expect("should never fail to take shutdown handle"); - +impl Checks { + // consumes self + async fn run_inner(self, context: SourceContext, global_shutdown: ComponentShutdownHandle) -> Result<(), ()> { let mut listener_shutdown_coordinator = DynamicShutdownCoordinator::default(); - let Checks { - listeners, - runner, - check_run_tx, - check_stop_tx, - } = *self; + let Checks { listeners } = self; + let mut joinset = tokio::task::JoinSet::new(); + // Run the local task set. // For each listener, spawn a dedicated task to run it. for listener in listeners { let listener_context = listener::DirCheckListenerContext { shutdown_handle: listener_shutdown_coordinator.register(), listener, - check_run_tx: check_run_tx.clone(), - check_stop_tx: check_stop_tx.clone(), }; - tokio::spawn(process_listener(context.clone(), listener_context)); + joinset.spawn_local(process_listener(context.clone(), listener_context)); } - let runner_shutdown_handle = listener_shutdown_coordinator.register(); - tokio::spawn(async move { - // will never return - runner.run(runner_shutdown_handle).await; - }); - info!("Check source started."); - // Wait for the global shutdown signal, then notify listeners to shutdown. - global_shutdown.await; - info!("Stopping Check source..."); + select! { + j = joinset.join_next() => { + match j { + Some(Ok(_)) => { + debug!("Check source task set exited normally."); + } + Some(Err(e)) => { + error!("Check source task set exited unexpectedly: {:?}", e); + } + None => { + // set is empty, all good here + } + } + }, + _ = global_shutdown => { + info!("Stopping Check source..."); - listener_shutdown_coordinator.shutdown().await; + listener_shutdown_coordinator.shutdown().await; + } + } info!("Check source stopped."); @@ -265,17 +279,49 @@ impl Source for Checks { } } +#[async_trait] +impl Source for Checks { + async fn run(mut self: Box, mut context: SourceContext) -> Result<(), ()> { + let global_shutdown = context + .take_shutdown_handle() + .expect("should never fail to take shutdown handle"); + thread::spawn(|| { + let rt = tokio::runtime::Builder::new_current_thread() + .thread_name("check-runner") + .enable_all() + .build() + .expect("Can't build runtime"); + + let local = tokio::task::LocalSet::new(); + local.block_on(&rt, async { + select! { + inner_res = self.run_inner(context, global_shutdown) => { + info!("Got inner res: {inner_res:?}"); + } + } + }) + }) + .join() + .expect("Can't join"); + + Ok(()) + } +} + async fn process_listener(source_context: SourceContext, listener_context: listener::DirCheckListenerContext) { let listener::DirCheckListenerContext { shutdown_handle, mut listener, - check_run_tx, - check_stop_tx, } = listener_context; tokio::pin!(shutdown_handle); let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); + let (schedule_check_tx, schedule_check_rx) = mpsc::channel(100); + let (unschedule_check_tx, unschedule_check_rx) = mpsc::channel(100); + let scheduler = scheduler::CheckScheduler::new(schedule_check_rx, unschedule_check_rx); // todo add shutdown handle + + tokio::task::spawn_local(scheduler.run()); info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); loop { @@ -293,8 +339,6 @@ async fn process_listener(source_context: SourceContext, listener_context: liste } } Some(new_entity) = new_entities.recv() => { - debug!("Received new check entity, {}", new_entity); - let check_request = match new_entity.to_runnable_request() { Ok(check_request) => check_request, Err(e) => { @@ -304,16 +348,11 @@ async fn process_listener(source_context: SourceContext, listener_context: liste }; info!("Running a check request: {check_request}"); - // send all checks instances for scheduling to the scheduler - // TODO(remy): send_to_scheduler(checks).await; - check_run_tx.send(check_request).await.expect("Could send"); + schedule_check_tx.send(check_request).await.expect("Couldn't send"); } Some(deleted_entity) = deleted_entities.recv() => { - debug!("Received deleted check entity {}", deleted_entity); - // TODO - try to stop running this check - // (low priority) - check_stop_tx.send(deleted_entity).await.expect("Could send"); + unschedule_check_tx.send(deleted_entity).await.expect("Couldn't send"); } } } diff --git a/lib/saluki-components/src/sources/checks/runner.rs b/lib/saluki-components/src/sources/checks/runner.rs deleted file mode 100644 index 6f4261bb..00000000 --- a/lib/saluki-components/src/sources/checks/runner.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{collections::HashMap, path::PathBuf, time::Duration}; - -use saluki_core::topology::shutdown::DynamicShutdownHandle; -use snafu::Snafu; -use tokio::{sync::mpsc, task::JoinHandle}; -use tracing::info; - -use super::{CheckInstanceConfiguration, CheckRequest, RunnableCheckRequest}; - -#[derive(Debug, Snafu)] -#[snafu(context(suffix(false)))] -pub enum Error {} - -pub struct CheckRunner { - new_checks_rx: mpsc::Receiver, - deleted_checks_rx: mpsc::Receiver, - // key here is the configuration that spawned this check - running: HashMap>>, -} - -impl CheckRunner { - pub fn new( - new_checks_rx: mpsc::Receiver, deleted_checks_rx: mpsc::Receiver, - ) -> Result { - Ok(CheckRunner { - new_checks_rx, - deleted_checks_rx, - running: HashMap::new(), - }) - } - pub async fn run(mut self, shutdown_handle: DynamicShutdownHandle) -> JoinHandle<()> { - tokio::spawn(async move { - tokio::pin!(shutdown_handle); - - loop { - tokio::select! { - _ = &mut shutdown_handle => { - info!("Got shutdown signal, shutting down now"); - break; - } - Some(check) = self.new_checks_rx.recv() => { - self.add_check(check).await; - } - Some(check) = self.deleted_checks_rx.recv() => { - self.delete_check(check).await; - } - else => { - break; // when channels are closed, exit - } - } - } - }) - } - - async fn add_check(&mut self, check: RunnableCheckRequest) { - info!("Adding check request {check}"); - let py_source = tokio::fs::read_to_string(&check.check_source_code).await.unwrap(); - let running = self.running.entry(check.check_source_code).or_default(); - for instance in check.check_request.instances { - let py_source = py_source.clone(); - let idx = running.len(); - let handle = tokio::spawn(async move { - let mut interval = - tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); - loop { - interval.tick().await; - // run check - info!("Running check instance {idx} on {}", py_source); - } - }); - running.push(handle); - } - } - async fn delete_check(&self, check: CheckRequest) { - info!("Deleting check request {check}"); - } -} diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs new file mode 100644 index 00000000..dad7e59d --- /dev/null +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -0,0 +1,120 @@ +use std::sync::Arc; + +use rustpython_vm::{compiler::Mode, PyObjectRef}; + +use super::*; + +struct CheckHandle { + id: PyObjectRef, +} + +pub struct CheckScheduler { + schedule_check_rx: mpsc::Receiver, + unschedule_check_rx: mpsc::Receiver, + interpreter: Arc, + running: HashMap>>, +} +impl CheckScheduler { + pub fn new( + schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, + ) -> Self { + let interpreter = Interpreter::with_init(Default::default(), |vm| { + vm.add_native_modules(rustpython_stdlib::get_module_inits()); + }); + + Self { + schedule_check_rx, + unschedule_check_rx, + interpreter: Arc::new(interpreter), + running: HashMap::new(), + } + } + + // consumes self + pub async fn run(mut self) { + loop { + select! { + Some(check) = self.schedule_check_rx.recv() => { + self.run_check(check).await; + } + Some(check) = self.unschedule_check_rx.recv() => { + self.stop_check(check).await; + } + } + } + } + + // compiles the python source code and instantiates it (??) into the VM + // returns an opaque handle to the check + fn register_check(&mut self, check_source_path: PathBuf) -> Result { + let py_source = std::fs::read_to_string(&check_source_path).unwrap(); + info!(?py_source, "Trying to run the following code"); + //let py_source = r#"print("hello world")"#; + let path_to_source = check_source_path + .as_os_str() + .to_str() + .unwrap_or("") + .to_string(); + + self.interpreter + .enter(|vm| { + let scope = vm.new_scope_with_builtins(); + let code_obj = vm + .compile(&py_source, Mode::Exec, path_to_source) + .map_err(|err| vm.new_syntax_error(&err, Some(&py_source))) + .unwrap(); + let run_res = vm.run_code_obj(code_obj, scope); + match run_res { + Ok(t) => { + info!(?t, "ran compiled pycheck"); + Ok(CheckHandle { id: t }) + } + Err(e) => { + error!(?e, "failed to run compiled pycheck"); + Err(e) + } + } + }) + .map_err(Error::from) + } + + // This function does 3 things + // 1. Registers the check source code and gets a handle + // 2. Starts a local task for each instance that + // queues a run of the check every min_collection_interval_ms + // 3. Stores the handles in the running hashmap + async fn run_check(&mut self, check: RunnableCheckRequest) { + // registry should probably queue off of checkhandle + //let current = self.running.entry(check.check_request.source.clone()).or_default(); + + let check_handle = self.register_check(check.check_source_code.clone()); + + for (idx, instance) in check.check_request.instances.iter().enumerate() { + let instance = instance.clone(); + let handle = tokio::task::spawn_local(async move { + let mut interval = + tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); + loop { + interval.tick().await; + // run check + info!("Running check instance {idx}"); + /* + self.queue_run_tx + .send(check_handle, instance.clone()) + .await + .expect("Could send"); + */ + } + }); + } + } + + async fn stop_check(&self, check: CheckRequest) { + info!("Deleting check request {check}"); + if let Some(running) = self.running.get(&check.source) { + for handle in running { + handle.abort(); + } + } + } +} From 3fe15a43cd4b4b024cbeeeb8385b87347bc87d6c Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 15 May 2024 21:38:11 +0000 Subject: [PATCH 11/47] VM now tries (but fails) to import the datadog_checks module --- Cargo.lock | 4 ++-- conf.d/simple.py | 2 -- conf.d/verysimple.py | 5 +++++ conf.d/verysimple.yaml | 7 ++++++ lib/saluki-components/Cargo.toml | 10 +++++++-- .../src/sources/checks/mod.rs | 2 -- .../src/sources/checks/scheduler.rs | 22 +++++++++++++++---- 7 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 conf.d/verysimple.py create mode 100644 conf.d/verysimple.yaml diff --git a/Cargo.lock b/Cargo.lock index 3d16074d..35ffda1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3959,9 +3959,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" diff --git a/conf.d/simple.py b/conf.d/simple.py index c952cd6f..6431e494 100644 --- a/conf.d/simple.py +++ b/conf.d/simple.py @@ -1,5 +1,3 @@ -import sys - from datadog_checks.checks import AgentCheck class ChurnCheck(AgentCheck): diff --git a/conf.d/verysimple.py b/conf.d/verysimple.py new file mode 100644 index 00000000..ed35ca51 --- /dev/null +++ b/conf.d/verysimple.py @@ -0,0 +1,5 @@ +import AgentCheck + +class ChurnCheck(AgentCheck): + def check(self, instance): + print("Hello world") diff --git a/conf.d/verysimple.yaml b/conf.d/verysimple.yaml new file mode 100644 index 00000000..5f502a55 --- /dev/null +++ b/conf.d/verysimple.yaml @@ -0,0 +1,7 @@ +instances: + - argument: something + - argument: something2 + - argument: something3 + - argument: something4 + - argument: something5 + - argument: something7 diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index d9ee8d70..29d154de 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -48,5 +48,11 @@ tower = { workspace = true } tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" -rustpython-vm = "0.3.1" -rustpython-stdlib = "0.3.1" + +[dependencies.rustpython-vm] +#path = "/home/ubuntu/dev/RustPython/vm", +version= "0.3.1" +features = ["importlib", "compiler"] +[dependencies.rustpython-stdlib] +#path = "/home/ubuntu/dev/RustPython/stdlib" +version = "0.3.1" diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index bcf74098..df22412f 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -55,8 +55,6 @@ impl From> for Er } } -// implement Error#from> - /// Checks source. /// /// Scans a directory for check configurations and emits them as things to run. diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index dad7e59d..1ddc4a56 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use rustpython_vm::{compiler::Mode, PyObjectRef}; +use rustpython_vm::{compiler::Mode, vm, PyObjectRef}; use super::*; @@ -11,21 +11,34 @@ struct CheckHandle { pub struct CheckScheduler { schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, - interpreter: Arc, + interpreter: Interpreter, running: HashMap>>, } impl CheckScheduler { pub fn new( schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, ) -> Self { - let interpreter = Interpreter::with_init(Default::default(), |vm| { + let settings = + vm::Settings::default().with_path(String::from("/home/ubuntu/dev/integrations-core/datadog_checks_base/")); + let interpreter = Interpreter::with_init(settings, |vm| { vm.add_native_modules(rustpython_stdlib::get_module_inits()); }); + interpreter.enter(|vm| match vm.import("datadog_checks", 0) { + Ok(_) => { + info!("Successfully imported datadog_checks"); + let class = vm.class("datadog_checks", "AgentCheck"); + info!(?class, "Grabbed the 'AgentCheck' class!") + } + Err(e) => { + vm.print_exception(e.clone()); + panic!("failed to import datadog_checks"); + } + }); Self { schedule_check_rx, unschedule_check_rx, - interpreter: Arc::new(interpreter), + interpreter, running: HashMap::new(), } } @@ -70,6 +83,7 @@ impl CheckScheduler { Ok(CheckHandle { id: t }) } Err(e) => { + vm.print_exception(e.clone()); error!(?e, "failed to run compiled pycheck"); Err(e) } From 26aec6ddf0f29442526ba8404089ad0ea4e56ad0 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 15 May 2024 21:44:56 +0000 Subject: [PATCH 12/47] remove rando check --- conf.d/verysimple.py | 5 ----- conf.d/verysimple.yaml | 7 ------- 2 files changed, 12 deletions(-) delete mode 100644 conf.d/verysimple.py delete mode 100644 conf.d/verysimple.yaml diff --git a/conf.d/verysimple.py b/conf.d/verysimple.py deleted file mode 100644 index ed35ca51..00000000 --- a/conf.d/verysimple.py +++ /dev/null @@ -1,5 +0,0 @@ -import AgentCheck - -class ChurnCheck(AgentCheck): - def check(self, instance): - print("Hello world") diff --git a/conf.d/verysimple.yaml b/conf.d/verysimple.yaml deleted file mode 100644 index 5f502a55..00000000 --- a/conf.d/verysimple.yaml +++ /dev/null @@ -1,7 +0,0 @@ -instances: - - argument: something - - argument: something2 - - argument: something3 - - argument: something4 - - argument: something5 - - argument: something7 From 2704636fcee79e00045d57ed1dd9fdbe24e07520 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 16 May 2024 18:10:15 +0000 Subject: [PATCH 13/47] adds pyo3 --- Cargo.lock | 1480 ++--------------- README.md | 15 +- lib/saluki-components/Cargo.toml | 9 +- .../src/sources/checks/mod.rs | 29 +- .../src/sources/checks/scheduler.rs | 202 ++- 5 files changed, 262 insertions(+), 1473 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35ffda1d..dbc05435 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - [[package]] name = "addr2line" version = "0.21.0" @@ -23,12 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "agent-data-plane" version = "0.1.0" @@ -95,12 +83,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "async-channel" version = "2.3.0" @@ -168,7 +150,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -185,7 +167,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -198,12 +180,6 @@ dependencies = [ "futures-lite", ] -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - [[package]] name = "atomic" version = "0.6.0" @@ -219,17 +195,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -324,12 +289,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -361,7 +320,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.61", + "syn", "which", ] @@ -384,16 +343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9990737a6d5740ff51cdbbc0f0503015cb30c390f6623968281eb214a520cfc0" dependencies = [ "quote", - "syn 2.0.61", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", + "syn", ] [[package]] @@ -448,16 +398,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" -[[package]] -name = "caseless" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f" -dependencies = [ - "regex", - "unicode-normalization", -] - [[package]] name = "cc" version = "1.0.97" @@ -484,12 +424,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "chrono" version = "0.4.38" @@ -516,15 +450,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clipboard-win" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" -dependencies = [ - "error-code", -] - [[package]] name = "cmake" version = "0.1.50" @@ -567,12 +492,6 @@ dependencies = [ "tonic-build", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -622,12 +541,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -638,15 +551,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - [[package]] name = "datadog-protos" version = "0.1.0" @@ -682,19 +586,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - [[package]] name = "digest" version = "0.10.7" @@ -703,19 +594,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", -] - -[[package]] -name = "dns-lookup" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" -dependencies = [ - "cfg-if", - "libc", - "socket2", - "windows-sys 0.48.0", ] [[package]] @@ -724,12 +602,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - [[package]] name = "either" version = "1.11.0" @@ -742,12 +614,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "equivalent" version = "1.0.1" @@ -764,12 +630,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "error-code" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" - [[package]] name = "event-listener" version = "4.0.3" @@ -812,36 +672,19 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "exitcode" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" - [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "figment" version = "0.10.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d032832d74006f99547004d49410a4b4218e4c33382d56ca3ff89df74f86b953" dependencies = [ - "atomic 0.6.0", + "atomic", "pear", "serde", "serde_json", @@ -962,7 +805,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -1005,25 +848,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1031,10 +855,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -1087,12 +909,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" - [[package]] name = "hashbrown" version = "0.12.3" @@ -1120,33 +936,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hexf-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" - [[package]] name = "home" version = "0.5.9" @@ -1412,22 +1207,16 @@ dependencies = [ ] [[package]] -name = "inlinable_string" -version = "0.1.15" +name = "indoc" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] -name = "is-macro" -version = "0.3.5" +name = "inlinable_string" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a85abdc13717906baccb5a1e435556ce0df215f242892f721dff62bf25288f" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 2.0.61", -] +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "itertools" @@ -1438,15 +1227,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1508,15 +1288,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - [[package]] name = "kube" version = "0.89.0" @@ -1582,12 +1353,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1600,36 +1365,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - [[package]] name = "libc" version = "0.2.154" @@ -1652,17 +1387,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "libsqlite3-sys" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1685,89 +1409,6 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "lz4_flex" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "mac_address" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa12182b93606fff55b70a5cfe6130eaf7407c2ea4f2c2bcc8b113b67c9928f" -dependencies = [ - "nix 0.28.0", - "winapi", -] - -[[package]] -name = "malachite" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ad951c9d45bd4c77f4c843844870dddeb2e332864226f5a62277498e1627b8" -dependencies = [ - "malachite-base", - "malachite-nz", - "malachite-q", -] - -[[package]] -name = "malachite-base" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d073a3d1e4e037975af5ef176a2632672e25e8ddbe8e1811745c2e0726b6ad94" -dependencies = [ - "hashbrown 0.14.5", - "itertools 0.11.0", - "libm", - "ryu", -] - -[[package]] -name = "malachite-bigint" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17703a19c80bbdd0b7919f0f104f3b0597f7de4fc4e90a477c15366a5ba03faa" -dependencies = [ - "derive_more", - "malachite", - "num-integer", - "num-traits", - "paste", -] - -[[package]] -name = "malachite-nz" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2546fc6ae29728079e87a2a0f011509e6060713b65e62ee46ba5d413b495ebc7" -dependencies = [ - "itertools 0.11.0", - "libm", - "malachite-base", -] - -[[package]] -name = "malachite-q" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad0ac879ecbb7e1cc8060b48696eebed0b0de8d922db4f8b378dfbc1bad806a" -dependencies = [ - "itertools 0.11.0", - "malachite-base", - "malachite-nz", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "match_cfg" version = "0.1.0" @@ -1783,12 +1424,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.3" @@ -1805,31 +1440,12 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1910,17 +1526,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] -name = "mt19937" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ca7f22ed370d5991a9caec16a83187e865bc8a532f889670337d5a5689e3a1" -dependencies = [ - "rand_core", -] - -[[package]] -name = "multimap" -version = "0.10.0" +name = "multimap" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" @@ -1952,40 +1559,6 @@ dependencies = [ "rand", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "noisy_float" version = "0.2.0" @@ -2055,31 +1628,10 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] -[[package]] -name = "num_enum" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.61", -] - [[package]] name = "num_threads" version = "0.1.7" @@ -2110,12 +1662,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "optional" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc" - [[package]] name = "ordered-float" version = "2.10.1" @@ -2140,16 +1686,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "page_size" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "parking" version = "2.2.0" @@ -2205,7 +1741,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -2255,7 +1791,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -2279,44 +1815,6 @@ dependencies = [ "indexmap 2.2.6", ] -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.5" @@ -2334,7 +1832,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -2360,23 +1858,6 @@ dependencies = [ "futures-io", ] -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "pmutil" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "portable-atomic" version = "1.6.0" @@ -2402,16 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.61", -] - -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit", + "syn", ] [[package]] @@ -2431,7 +1903,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", "version_check", "yansi", ] @@ -2479,7 +1951,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.61", + "syn", "tempfile", ] @@ -2493,7 +1965,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -2558,10 +2030,68 @@ dependencies = [ ] [[package]] -name = "puruspe" -version = "0.2.4" +name = "pyo3" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" +dependencies = [ + "anyhow", + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a1eed715f625eaa95fba5e049dcf7bc06fa396d6d2e55015b3764e234dfd3f" +checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] [[package]] name = "quanta" @@ -2587,22 +2117,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -2720,28 +2234,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "result-like" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810" -dependencies = [ - "result-like-derive", -] - -[[package]] -name = "result-like-derive" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42" -dependencies = [ - "pmutil", - "proc-macro2", - "quote", - "syn 1.0.109", - "syn-ext", -] - [[package]] name = "ring" version = "0.17.8" @@ -2769,15 +2261,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.34" @@ -2847,379 +2330,11 @@ dependencies = [ "untrusted 0.9.0", ] -[[package]] -name = "rustpython-ast" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c72612d246194023a643d342ac53e4118c300cbc3ada31ff1245e7636db860" -dependencies = [ - "is-macro", - "malachite-bigint", - "rustpython-literal", - "rustpython-parser-core", - "static_assertions", -] - -[[package]] -name = "rustpython-codegen" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964cbc9d7870b4d092eecd66049ab0cd93fb5f3f40a408f78bf4415a5d25d3a3" -dependencies = [ - "ahash", - "bitflags 2.5.0", - "indexmap 2.2.6", - "itertools 0.11.0", - "log", - "num-complex", - "num-traits", - "rustpython-ast", - "rustpython-compiler-core", - "rustpython-parser-core", -] - -[[package]] -name = "rustpython-common" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5b1bae2d3bd9c18a74a835a496b2f7dedcea692e66d47ff56a1dd100ed35c9" -dependencies = [ - "ascii", - "bitflags 2.5.0", - "bstr", - "cfg-if", - "itertools 0.11.0", - "libc", - "lock_api", - "malachite-base", - "malachite-bigint", - "malachite-q", - "num-complex", - "num-traits", - "once_cell", - "radium", - "rand", - "rustpython-format", - "siphasher", - "volatile", - "widestring", -] - -[[package]] -name = "rustpython-compiler" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad4849236178a0296808c30fca6f7bf8030b57a9b6e85223129b23ae5bfb9fa3" -dependencies = [ - "rustpython-codegen", - "rustpython-compiler-core", - "rustpython-parser", -] - -[[package]] -name = "rustpython-compiler-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf4f4dabec9dec0250c2544bab9229428c7a6af04dba34508476e975fbbc3a1" -dependencies = [ - "bitflags 2.5.0", - "itertools 0.11.0", - "lz4_flex", - "malachite-bigint", - "num-complex", - "rustpython-parser-core", -] - -[[package]] -name = "rustpython-derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcac29b3573c59fa22e2d7457b12c07e502b7dfbd38ce62bdd23a25f3d8a8eb" -dependencies = [ - "rustpython-compiler", - "rustpython-derive-impl", - "syn 1.0.109", -] - -[[package]] -name = "rustpython-derive-impl" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe0c74ae690e16c0d08a07236d5736989a3c9a5fc793e04f5ee4b8fbd3c60a9" -dependencies = [ - "itertools 0.11.0", - "maplit", - "once_cell", - "proc-macro2", - "quote", - "rustpython-compiler-core", - "rustpython-doc", - "rustpython-parser-core", - "syn 1.0.109", - "syn-ext", - "textwrap", -] - -[[package]] -name = "rustpython-doc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885d19895d9d29656a8a2b33e967a482b92f3d891b4fd923e40849714051bcd" -dependencies = [ - "once_cell", -] - -[[package]] -name = "rustpython-format" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba07bbc520a3884d6c7c91bdcc2d49b6645b8d8ea6d13c4bf8300a7ddea3ad72" -dependencies = [ - "bitflags 2.5.0", - "itertools 0.11.0", - "malachite-bigint", - "num-traits", - "rustpython-literal", -] - -[[package]] -name = "rustpython-literal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443cbcbd1a87a27be77e24b16654716900dc9151db57c02380a2b480ecce72c0" -dependencies = [ - "hexf-parse", - "is-macro", - "lexical-parse-float", - "num-traits", - "unic-ucd-category", -] - -[[package]] -name = "rustpython-parser" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e73421499eff08722b979d8494c3f1bc035908931a28607a9f17be6143ac11" -dependencies = [ - "anyhow", - "is-macro", - "itertools 0.11.0", - "lalrpop-util", - "log", - "malachite-bigint", - "num-traits", - "phf", - "phf_codegen", - "rustc-hash", - "rustpython-ast", - "rustpython-parser-core", - "tiny-keccak", - "unic-emoji-char", - "unic-ucd-ident", - "unicode_names2", -] - -[[package]] -name = "rustpython-parser-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f9465e822d45e05304c08f168dd1ffea05d8d95b7a4e18651ef1acb4df097f" -dependencies = [ - "is-macro", - "memchr", - "rustpython-parser-vendored", -] - -[[package]] -name = "rustpython-parser-vendored" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e43398f39b5e4ce129cb95a8d7b87c6378b056d66c978da86ea2296d861c8f" -dependencies = [ - "memchr", - "once_cell", -] - -[[package]] -name = "rustpython-sre_engine" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf3bb085265414a74cc9c6e90316ea6e423f582c3ca86c34430a39b9f150d93" -dependencies = [ - "bitflags 2.5.0", - "num_enum", - "optional", -] - -[[package]] -name = "rustpython-stdlib" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c175766165785ef4e528e7ee698384b913f20257eed9cd112f0f785ce03578" -dependencies = [ - "adler32", - "ahash", - "ascii", - "base64 0.13.1", - "blake2", - "cfg-if", - "crc32fast", - "crossbeam-utils", - "csv-core", - "digest", - "dns-lookup", - "dyn-clone", - "flate2", - "gethostname", - "hex", - "itertools 0.11.0", - "libc", - "libsqlite3-sys", - "mac_address", - "malachite-bigint", - "md-5", - "memchr", - "memmap2", - "mt19937", - "nix 0.27.1", - "num-complex", - "num-integer", - "num-traits", - "num_enum", - "once_cell", - "page_size", - "parking_lot", - "paste", - "puruspe", - "rand", - "rand_core", - "rustpython-common", - "rustpython-derive", - "rustpython-vm", - "schannel", - "sha-1", - "sha2", - "sha3", - "socket2", - "system-configuration", - "termios", - "ucd", - "unic-char-property", - "unic-normal", - "unic-ucd-age", - "unic-ucd-bidi", - "unic-ucd-category", - "unic-ucd-ident", - "unicode-casing", - "unicode_names2", - "uuid", - "widestring", - "winapi", - "windows-sys 0.52.0", - "xml-rs", -] - -[[package]] -name = "rustpython-vm" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d304aecce4f914def54abf331e458ee99f611b862580acb0c448e67a8237d28d" -dependencies = [ - "ahash", - "ascii", - "atty", - "bitflags 2.5.0", - "bstr", - "caseless", - "cfg-if", - "chrono", - "crossbeam-utils", - "exitcode", - "getrandom", - "glob", - "half", - "hex", - "indexmap 2.2.6", - "is-macro", - "itertools 0.11.0", - "libc", - "log", - "malachite-bigint", - "memchr", - "memoffset", - "nix 0.27.1", - "num-complex", - "num-integer", - "num-traits", - "num_cpus", - "num_enum", - "once_cell", - "optional", - "parking_lot", - "paste", - "rand", - "result-like", - "rustc_version", - "rustpython-ast", - "rustpython-codegen", - "rustpython-common", - "rustpython-compiler", - "rustpython-compiler-core", - "rustpython-derive", - "rustpython-format", - "rustpython-literal", - "rustpython-parser", - "rustpython-parser-core", - "rustpython-sre_engine", - "rustyline", - "schannel", - "static_assertions", - "strum", - "strum_macros", - "thiserror", - "thread_local", - "timsort", - "uname", - "unic-ucd-bidi", - "unic-ucd-category", - "unic-ucd-ident", - "unicode-casing", - "unicode_names2", - "wasm-bindgen", - "which", - "widestring", - "windows", - "windows-sys 0.52.0", - "winreg", -] - [[package]] name = "rustversion" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" - -[[package]] -name = "rustyline" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix 0.28.0", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "windows-sys 0.52.0", -] +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "ryu" @@ -3267,10 +2382,9 @@ dependencies = [ "paste", "pin-project", "protobuf", + "pyo3", "quanta", "rustls", - "rustpython-stdlib", - "rustpython-vm", "saluki-config", "saluki-core", "saluki-env", @@ -3479,12 +2593,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - [[package]] name = "serde" version = "1.0.201" @@ -3512,7 +2620,7 @@ checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -3539,17 +2647,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.8" @@ -3561,16 +2658,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -3615,12 +2702,6 @@ dependencies = [ "similar", ] -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "slab" version = "0.4.9" @@ -3654,7 +2735,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -3673,48 +2754,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.61" @@ -3726,15 +2771,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn-ext" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6" -dependencies = [ - "syn 1.0.109", -] - [[package]] name = "sync_wrapper" version = "0.1.2" @@ -3742,25 +2778,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "target-lexicon" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tempfile" @@ -3774,21 +2795,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "termios" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" -dependencies = [ - "libc", -] - -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" - [[package]] name = "thiserror" version = "1.0.60" @@ -3806,7 +2812,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -3852,21 +2858,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "timsort" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639ce8ef6d2ba56be0383a94dd13b92138d58de44c62618303bb798fa92bdc00" - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3918,7 +2909,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -3957,23 +2948,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml_datetime" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" - -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - [[package]] name = "tonic" version = "0.11.0" @@ -4011,7 +2985,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -4085,7 +3059,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -4133,43 +3107,18 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4fa6e588762366f1eb4991ce59ad1b93651d0b769dfb4e4d1c5c4b943d1159" - [[package]] name = "ucd-trie" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - [[package]] name = "unarray" version = "0.1.4" @@ -4185,134 +3134,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-emoji-char" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-normal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09d64d33589a94628bc2aeb037f35c2e25f3f049c7348b5aa5580b48e6bba62" -dependencies = [ - "unic-ucd-normal", -] - -[[package]] -name = "unic-ucd-age" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8cfdfe71af46b871dc6af2c24fcd360e2f3392ee4c5111877f2947f311671c" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-bidi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-category" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" -dependencies = [ - "matches", - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-hangul" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1dc690e19010e1523edb9713224cba5ef55b54894fe33424439ec9a40c0054" -dependencies = [ - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-normal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86aed873b8202d22b13859dda5fe7c001d271412c31d411fd9b827e030569410" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-hangul", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" -[[package]] -name = "unicode-casing" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623f59e6af2a98bdafeb93fa277ac8e1e40440973001ca15cf4ae1541cd16d56" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -4335,32 +3162,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] -name = "unicode-width" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" - -[[package]] -name = "unicode_names2" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addeebf294df7922a1164f729fb27ebbbcea99cc32b3bf08afab62757f707677" -dependencies = [ - "phf", - "unicode_names2_generator", -] - -[[package]] -name = "unicode_names2_generator" -version = "1.2.2" +name = "unindent" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f444b8bba042fe3c1251ffaca35c603f2dc2ccc08d595c65a8c4f76f3e8426c0" -dependencies = [ - "getopts", - "log", - "phf_codegen", - "rand", -] +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "unsafe-libyaml" @@ -4391,59 +3196,18 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "atomic 0.5.3", - "getrandom", - "rand", - "uuid-macro-internal", -] - -[[package]] -name = "uuid-macro-internal" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.61", -] - [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "volatile" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e76fae08f03f96e166d2dfda232190638c10e0383841252416f9cfe2ae60e6" - [[package]] name = "want" version = "0.3.1" @@ -4480,7 +3244,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.61", + "syn", "wasm-bindgen-shared", ] @@ -4502,7 +3266,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4535,12 +3299,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -4563,16 +3321,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.5", -] - [[package]] name = "windows-core" version = "0.52.0" @@ -4721,30 +3469,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "xml-rs" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" - [[package]] name = "yansi" version = "1.0.1" @@ -4768,7 +3492,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] diff --git a/README.md b/README.md index fa399faa..ac0ed1e6 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,19 @@ All remaining crates are part of Saluki itself, and all have a name with the pre - `lib/saluki-io`: core I/O primitives for networking (TCP/UDP/UDS), serialization (codecs and framers), compression, I/O-specific buffers, as well as some common codec implementations (e.g. DogStatsD) +### Saluki Components +#### Checks +Experimental support for python checks is present if there is a local `./conf.d` +folder present with a valid python check and yaml config file with the same +name. + +pyo3 is used to provide python support, which works off of your system's python +install, `sudo apt install libpython3-devel` (todo double check this) + +a venv is present that must have the following installed: +- `pip3 install datadog_checks_base` +- `pip3 install datadog_checks_base[deps]` + ## Contributing If you find an issue with this package and have a fix, or simply want to report it, please review our @@ -55,4 +68,4 @@ Please refer to our [Security Policy][security-policy] if you believe you have f [ddsketch]: https://www.vldb.org/pvldb/vol12/p2195-masson.pdf [ddsketch-agent]: https://github.com/DataDog/opentelemetry-mapping-go/blob/main/pkg/quantile/sparse.go [contributing]: CONTRIBUTING.md -[security-policy]: SECURITY.md \ No newline at end of file +[security-policy]: SECURITY.md diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 29d154de..9d0b3eb8 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -48,11 +48,4 @@ tower = { workspace = true } tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" - -[dependencies.rustpython-vm] -#path = "/home/ubuntu/dev/RustPython/vm", -version= "0.3.1" -features = ["importlib", "compiler"] -[dependencies.rustpython-stdlib] -#path = "/home/ubuntu/dev/RustPython/stdlib" -version = "0.3.1" +pyo3 = { version = "0.21.2", features = ["anyhow"] } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index df22412f..eee861b7 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,11 +1,9 @@ use async_trait::async_trait; use async_walkdir::{DirEntry, Filtering, WalkDir}; use futures::StreamExt as _; -use rustpython_vm::Interpreter; use saluki_config::GenericConfiguration; use saluki_core::{ components::{Source, SourceBuilder, SourceContext}, - prelude::ErasedError, topology::{ shutdown::{ ComponentShutdownCoordinator, ComponentShutdownHandle, DynamicShutdownCoordinator, DynamicShutdownHandle, @@ -13,6 +11,7 @@ use saluki_core::{ OutputDefinition, }, }; +use saluki_error::GenericError; use saluki_event::DataType; use serde::Deserialize; use snafu::Snafu; @@ -47,14 +46,6 @@ enum Error { }, } -impl From> for Error { - fn from(err: rustpython_vm::PyRef) -> Self { - Error::Python { - reason: format!("Error: {:?}", err), - } - } -} - /// Checks source. /// /// Scans a directory for check configurations and emits them as things to run. @@ -188,7 +179,7 @@ impl Display for CheckSource { impl ChecksConfiguration { /// Creates a new `ChecksConfiguration` from the given configuration. - pub fn from_configuration(config: &GenericConfiguration) -> Result { + pub fn from_configuration(config: &GenericConfiguration) -> Result { Ok(config.as_typed()?) } @@ -205,7 +196,7 @@ impl ChecksConfiguration { #[async_trait] impl SourceBuilder for ChecksConfiguration { - async fn build(&self) -> Result, Box> { + async fn build(&self) -> Result, GenericError> { //let (check_run_tx, check_run_rx) = mpsc::channel(100); //let (check_stop_tx, check_stop_rx) = mpsc::channel(100); //let runner = CheckRunner::new(check_run_rx, check_stop_rx)?; @@ -315,11 +306,11 @@ async fn process_listener(source_context: SourceContext, listener_context: liste let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - let (schedule_check_tx, schedule_check_rx) = mpsc::channel(100); - let (unschedule_check_tx, unschedule_check_rx) = mpsc::channel(100); - let scheduler = scheduler::CheckScheduler::new(schedule_check_rx, unschedule_check_rx); // todo add shutdown handle + //let (schedule_check_tx, schedule_check_rx) = mpsc::channel::(100); + //let (unschedule_check_tx, unschedule_check_rx) = mpsc::channel::(100); + let scheduler = scheduler::CheckScheduler::new(); // todo add shutdown handle - tokio::task::spawn_local(scheduler.run()); + //tokio::task::spawn_local(scheduler.run()); info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); loop { @@ -347,10 +338,12 @@ async fn process_listener(source_context: SourceContext, listener_context: liste info!("Running a check request: {check_request}"); - schedule_check_tx.send(check_request).await.expect("Couldn't send"); + //schedule_check_tx.send(check_request).await.expect("Couldn't send"); + scheduler.run_check(check_request); } Some(deleted_entity) = deleted_entities.recv() => { - unschedule_check_tx.send(deleted_entity).await.expect("Couldn't send"); + //unschedule_check_tx.send(deleted_entity).await.expect("Couldn't send"); + scheduler.stop_check(deleted_entity); } } } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 1ddc4a56..6ed32511 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,95 +1,150 @@ -use std::sync::Arc; - -use rustpython_vm::{compiler::Mode, vm, PyObjectRef}; - use super::*; +use pyo3::prelude::*; +use pyo3::types::PyAnyMethods; +use pyo3::types::PyType; +use saluki_error::{generic_error, GenericError}; -struct CheckHandle { - id: PyObjectRef, +struct CheckHandle<'py> { + id: Bound<'py, PyAny>, } -pub struct CheckScheduler { - schedule_check_rx: mpsc::Receiver, - unschedule_check_rx: mpsc::Receiver, - interpreter: Interpreter, - running: HashMap>>, +pub struct CheckScheduler<'py> { + //schedule_check_rx: mpsc::Receiver, + //unschedule_check_rx: mpsc::Receiver, + running: HashMap, Vec>)>, + source_to_handle: HashMap>, } -impl CheckScheduler { - pub fn new( - schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, +impl<'py> CheckScheduler<'py> { + pub fn new(//schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, ) -> Self { - let settings = - vm::Settings::default().with_path(String::from("/home/ubuntu/dev/integrations-core/datadog_checks_base/")); - let interpreter = Interpreter::with_init(settings, |vm| { - vm.add_native_modules(rustpython_stdlib::get_module_inits()); - }); - interpreter.enter(|vm| match vm.import("datadog_checks", 0) { - Ok(_) => { - info!("Successfully imported datadog_checks"); - let class = vm.class("datadog_checks", "AgentCheck"); - info!(?class, "Grabbed the 'AgentCheck' class!") - } - Err(e) => { - vm.print_exception(e.clone()); - panic!("failed to import datadog_checks"); - } + // todo, add in apis that python checks expect + //pyo3::append_to_inittab!(pylib_module); + + pyo3::prepare_freethreaded_python(); + + // Sanity test for python environment before executing + pyo3::Python::with_gil(|py| { + let modd = match py.import_bound("datadog_checks.checks") { + Ok(m) => m, + Err(e) => { + let traceback = e + .traceback_bound(py) + .expect("Traceback should be present on this error"); + error!(%e, "Could not import datadog_checks module. traceback: {}", traceback.format().expect("Could format traceback")); + return; + } + }; + let class = match modd.getattr("AgentCheck") { + Ok(c) => c, + Err(e) => { + let traceback = e + .traceback_bound(py) + .expect("Traceback should be present on this error"); + error!(%e, "Could not get AgentCheck class. traceback: {}", traceback.format().expect("Could format traceback")); + return; + } + }; + info!(%class, %modd, "Was able to import AgentCheck!"); }); Self { - schedule_check_rx, - unschedule_check_rx, - interpreter, + //schedule_check_rx, + //unschedule_check_rx, running: HashMap::new(), + source_to_handle: HashMap::new(), } } + /* // consumes self pub async fn run(mut self) { + let CheckScheduler { + mut schedule_check_rx, + mut unschedule_check_rx, + .. + } = self; + loop { select! { - Some(check) = self.schedule_check_rx.recv() => { + Some(check) = schedule_check_rx.recv() => { self.run_check(check).await; } - Some(check) = self.unschedule_check_rx.recv() => { + Some(check) = unschedule_check_rx.recv() => { self.stop_check(check).await; } } } } + */ // compiles the python source code and instantiates it (??) into the VM // returns an opaque handle to the check - fn register_check(&mut self, check_source_path: PathBuf) -> Result { + fn register_check(&mut self, check_source_path: PathBuf) -> Result { let py_source = std::fs::read_to_string(&check_source_path).unwrap(); - info!(?py_source, "Trying to run the following code"); - //let py_source = r#"print("hello world")"#; - let path_to_source = check_source_path - .as_os_str() - .to_str() - .unwrap_or("") - .to_string(); - - self.interpreter - .enter(|vm| { - let scope = vm.new_scope_with_builtins(); - let code_obj = vm - .compile(&py_source, Mode::Exec, path_to_source) - .map_err(|err| vm.new_syntax_error(&err, Some(&py_source))) - .unwrap(); - let run_res = vm.run_code_obj(code_obj, scope); - match run_res { - Ok(t) => { - info!(?t, "ran compiled pycheck"); - Ok(CheckHandle { id: t }) - } - Err(e) => { - vm.print_exception(e.clone()); - error!(?e, "failed to run compiled pycheck"); - Err(e) + + pyo3::Python::with_gil(|py| { + let locals = pyo3::types::PyDict::new_bound(py); + + match py.run_bound(&py_source, None, Some(&locals)) { + Ok(c) => {} + Err(e) => { + let traceback = e + .traceback_bound(py) + .expect("Traceback should be present on this error"); + error!(%e, "Could not compile check source. traceback: {}", traceback.format().expect("Could format traceback")); + return Err(generic_error!("Could not compile check source")); + } + }; + // the locals should now contain the class that this check defines + // lets make the simplifying assumption that there is only one + let base_class = locals + .get_item("AgentCheck") + .expect("Could not get 'AgentCheck' class") + .unwrap(); + /* + for (key, value) in locals.iter() { + if let Ok(class_obj) = value.downcast::() { + match class_obj.is_subclass(&base_class) { + Ok(true) => { + info!(%key, "Found class that is a subclass of AgentCheck"); + } + _ => {} } } - }) - .map_err(Error::from) + } */ + let checks = locals + .iter() + .filter(|(key, value)| { + if let Ok(class_obj) = value.downcast::() { + if class_obj.is(&base_class) { + // skip the base class + return false; + } + if let Ok(true) = class_obj.is_subclass(&base_class) { + return true; + } + false + } else { + false + } + }) + .collect::>(); + + if checks.len() == 0 { + return Err(generic_error!("No checks found in source")); + } + if checks.len() >= 2 { + return Err(generic_error!("Multiple checks found in source")); + } + let (check_key, check_value) = &checks[0]; + info!( + "For check source {}, found check: {}", + check_source_path.display(), + check_key + ); + //let class_ref = check_value.unbind(); + return Ok(CheckHandle { id: *check_value }); + }) } // This function does 3 things @@ -97,11 +152,15 @@ impl CheckScheduler { // 2. Starts a local task for each instance that // queues a run of the check every min_collection_interval_ms // 3. Stores the handles in the running hashmap - async fn run_check(&mut self, check: RunnableCheckRequest) { + pub fn run_check(&'py mut self, check: RunnableCheckRequest) -> Result<(), GenericError> { // registry should probably queue off of checkhandle //let current = self.running.entry(check.check_request.source.clone()).or_default(); - let check_handle = self.register_check(check.check_source_code.clone()); + let check_handle = self.register_check(check.check_source_code.clone())?; + let check_handle_hash = check_handle.id.hash()?; + self.running + .entry(check_handle_hash) + .or_insert((check_handle, Vec::new())); for (idx, instance) in check.check_request.instances.iter().enumerate() { let instance = instance.clone(); @@ -121,14 +180,21 @@ impl CheckScheduler { } }); } + + Ok(()) } - async fn stop_check(&self, check: CheckRequest) { + pub fn stop_check(&self, check: CheckRequest) { info!("Deleting check request {check}"); - if let Some(running) = self.running.get(&check.source) { - for handle in running { - handle.abort(); + if let Some(check_handle) = self.source_to_handle.get(&check.source) { + let check_handle_hash = check_handle.id.hash().expect("Could hash"); + if let Some(running) = self.running.get(&check_handle_hash) { + for handle in running.1.iter() { + handle.abort(); + } } } + // todo delete stuff out of the containers + // and stop/release the class if its not being used } } From 6d384be1e5b661148e2f15d6ebc2a9ba9197f39d Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 16 May 2024 19:12:15 +0000 Subject: [PATCH 14/47] Corrects the lifetime mgmnt of py check classes --- .../src/sources/checks/mod.rs | 2 +- .../src/sources/checks/scheduler.rs | 76 +++++-------------- 2 files changed, 18 insertions(+), 60 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index eee861b7..4f9d664b 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -308,7 +308,7 @@ async fn process_listener(source_context: SourceContext, listener_context: liste //let (schedule_check_tx, schedule_check_rx) = mpsc::channel::(100); //let (unschedule_check_tx, unschedule_check_rx) = mpsc::channel::(100); - let scheduler = scheduler::CheckScheduler::new(); // todo add shutdown handle + let mut scheduler = scheduler::CheckScheduler::new(); // todo add shutdown handle //tokio::task::spawn_local(scheduler.run()); info!("Check listener started."); diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 6ed32511..b5f5b987 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -4,17 +4,15 @@ use pyo3::types::PyAnyMethods; use pyo3::types::PyType; use saluki_error::{generic_error, GenericError}; -struct CheckHandle<'py> { - id: Bound<'py, PyAny>, +struct CheckHandle { + id: Py, } -pub struct CheckScheduler<'py> { - //schedule_check_rx: mpsc::Receiver, - //unschedule_check_rx: mpsc::Receiver, - running: HashMap, Vec>)>, - source_to_handle: HashMap>, +pub struct CheckScheduler { + running: HashMap>)>, } -impl<'py> CheckScheduler<'py> { + +impl CheckScheduler { pub fn new(//schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, ) -> Self { // todo, add in apis that python checks expect @@ -48,34 +46,9 @@ impl<'py> CheckScheduler<'py> { }); Self { - //schedule_check_rx, - //unschedule_check_rx, running: HashMap::new(), - source_to_handle: HashMap::new(), - } - } - - /* - // consumes self - pub async fn run(mut self) { - let CheckScheduler { - mut schedule_check_rx, - mut unschedule_check_rx, - .. - } = self; - - loop { - select! { - Some(check) = schedule_check_rx.recv() => { - self.run_check(check).await; - } - Some(check) = unschedule_check_rx.recv() => { - self.stop_check(check).await; - } - } } } - */ // compiles the python source code and instantiates it (??) into the VM // returns an opaque handle to the check @@ -95,23 +68,10 @@ impl<'py> CheckScheduler<'py> { return Err(generic_error!("Could not compile check source")); } }; - // the locals should now contain the class that this check defines - // lets make the simplifying assumption that there is only one let base_class = locals .get_item("AgentCheck") .expect("Could not get 'AgentCheck' class") .unwrap(); - /* - for (key, value) in locals.iter() { - if let Ok(class_obj) = value.downcast::() { - match class_obj.is_subclass(&base_class) { - Ok(true) => { - info!(%key, "Found class that is a subclass of AgentCheck"); - } - _ => {} - } - } - } */ let checks = locals .iter() .filter(|(key, value)| { @@ -130,7 +90,7 @@ impl<'py> CheckScheduler<'py> { }) .collect::>(); - if checks.len() == 0 { + if checks.is_empty() { return Err(generic_error!("No checks found in source")); } if checks.len() >= 2 { @@ -142,8 +102,8 @@ impl<'py> CheckScheduler<'py> { check_source_path.display(), check_key ); - //let class_ref = check_value.unbind(); - return Ok(CheckHandle { id: *check_value }); + let unbound = check_value.as_unbound(); + Ok(CheckHandle { id: unbound.clone() }) }) } @@ -152,14 +112,14 @@ impl<'py> CheckScheduler<'py> { // 2. Starts a local task for each instance that // queues a run of the check every min_collection_interval_ms // 3. Stores the handles in the running hashmap - pub fn run_check(&'py mut self, check: RunnableCheckRequest) -> Result<(), GenericError> { + pub fn run_check(&mut self, check: RunnableCheckRequest) -> Result<(), GenericError> { // registry should probably queue off of checkhandle //let current = self.running.entry(check.check_request.source.clone()).or_default(); let check_handle = self.register_check(check.check_source_code.clone())?; - let check_handle_hash = check_handle.id.hash()?; - self.running - .entry(check_handle_hash) + let running_entry = self + .running + .entry(check.check_request.source) .or_insert((check_handle, Vec::new())); for (idx, instance) in check.check_request.instances.iter().enumerate() { @@ -179,6 +139,7 @@ impl<'py> CheckScheduler<'py> { */ } }); + running_entry.1.push(handle); } Ok(()) @@ -186,12 +147,9 @@ impl<'py> CheckScheduler<'py> { pub fn stop_check(&self, check: CheckRequest) { info!("Deleting check request {check}"); - if let Some(check_handle) = self.source_to_handle.get(&check.source) { - let check_handle_hash = check_handle.id.hash().expect("Could hash"); - if let Some(running) = self.running.get(&check_handle_hash) { - for handle in running.1.iter() { - handle.abort(); - } + if let Some((check_handle, running)) = self.running.get(&check.source) { + for handle in running.iter() { + handle.abort(); } } // todo delete stuff out of the containers From 50be2601ea29c430aa24201b9aeef87f335f3317 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 16 May 2024 19:43:50 +0000 Subject: [PATCH 15/47] Checks actually run! --- .../src/sources/checks/scheduler.rs | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index b5f5b987..4aa9184a 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,6 +1,8 @@ use super::*; +use pyo3::prelude::PyAnyMethods; use pyo3::prelude::*; -use pyo3::types::PyAnyMethods; +use pyo3::types::PyDict; +use pyo3::types::PyList; use pyo3::types::PyType; use saluki_error::{generic_error, GenericError}; @@ -8,6 +10,12 @@ struct CheckHandle { id: Py, } +impl Clone for CheckHandle { + fn clone(&self) -> Self { + Self { id: self.id.clone() } + } +} + pub struct CheckScheduler { running: HashMap>)>, } @@ -102,8 +110,9 @@ impl CheckScheduler { check_source_path.display(), check_key ); - let unbound = check_value.as_unbound(); - Ok(CheckHandle { id: unbound.clone() }) + Ok(CheckHandle { + id: check_value.as_unbound().clone(), + }) }) } @@ -120,10 +129,12 @@ impl CheckScheduler { let running_entry = self .running .entry(check.check_request.source) - .or_insert((check_handle, Vec::new())); + .or_insert((check_handle.clone(), Vec::new())); for (idx, instance) in check.check_request.instances.iter().enumerate() { let instance = instance.clone(); + + let check_handle = check_handle.clone(); let handle = tokio::task::spawn_local(async move { let mut interval = tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); @@ -131,12 +142,32 @@ impl CheckScheduler { interval.tick().await; // run check info!("Running check instance {idx}"); - /* - self.queue_run_tx - .send(check_handle, instance.clone()) - .await - .expect("Could send"); - */ + pyo3::Python::with_gil(|py| { + let instance_as_pydict = PyDict::new_bound(py); + instance_as_pydict.set_item("min_collection_interval_ms", instance.min_collection_interval_ms); + + let instance_list = PyList::new_bound(py, &[instance_as_pydict]); + let kwargs = PyDict::new_bound(py); + kwargs.set_item("name", "placeholder_check_name"); + kwargs.set_item("init_config", PyDict::new_bound(py)); // todo this is in the check request maybe + kwargs.set_item("instances", instance_list); + + let check_ref = &check_handle.id; + let pycheck = match check_ref.call_bound(py, (), Some(&kwargs)) { + Ok(c) => c, + Err(e) => { + let traceback = e + .traceback_bound(py) + .expect("Traceback should be present on this error"); + error!(%e, "Could not instantiate check. traceback: {}", traceback.format().expect("Could format traceback")); + return; + } + }; + // 'run' method invokes 'check' with the instance we initialized with + // ref https://github.com/DataDog/integrations-core/blob/bc3b1c3496e79aa1b75ebcc9ef1c2a2b26487ebd/datadog_checks_base/datadog_checks/base/checks/base.py#L1197 + let result = pycheck.call_method0(py, "run").unwrap(); + info!("Result: {:?}", result); + }) } }); running_entry.1.push(handle); From 26f04bcedc5e4bb77914ef82ed242b220548da87 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 16 May 2024 19:51:40 +0000 Subject: [PATCH 16/47] Don't need 2nd tokio runtime anymore, pyo3 doesn't store an interpreter --- .../src/sources/checks/mod.rs | 46 ++++++------------- .../src/sources/checks/scheduler.rs | 25 ++++++---- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 4f9d664b..de90835f 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -5,9 +5,7 @@ use saluki_config::GenericConfiguration; use saluki_core::{ components::{Source, SourceBuilder, SourceContext}, topology::{ - shutdown::{ - ComponentShutdownCoordinator, ComponentShutdownHandle, DynamicShutdownCoordinator, DynamicShutdownHandle, - }, + shutdown::{ComponentShutdownHandle, DynamicShutdownCoordinator, DynamicShutdownHandle}, OutputDefinition, }, }; @@ -18,7 +16,6 @@ use snafu::Snafu; use std::{ collections::HashMap, path::{Path, PathBuf}, - thread, }; use std::{collections::HashSet, io}; use std::{fmt::Display, time::Duration}; @@ -236,7 +233,7 @@ impl Checks { listener, }; - joinset.spawn_local(process_listener(context.clone(), listener_context)); + joinset.spawn(process_listener(context.clone(), listener_context)); } info!("Check source started."); @@ -274,26 +271,13 @@ impl Source for Checks { let global_shutdown = context .take_shutdown_handle() .expect("should never fail to take shutdown handle"); - thread::spawn(|| { - let rt = tokio::runtime::Builder::new_current_thread() - .thread_name("check-runner") - .enable_all() - .build() - .expect("Can't build runtime"); - - let local = tokio::task::LocalSet::new(); - local.block_on(&rt, async { - select! { - inner_res = self.run_inner(context, global_shutdown) => { - info!("Got inner res: {inner_res:?}"); - } - } - }) - }) - .join() - .expect("Can't join"); - - Ok(()) + match self.run_inner(context, global_shutdown).await { + Ok(_) => Ok(()), + Err(e) => { + error!("Check source failed: {:?}", e); + Err(()) + } + } } } @@ -306,11 +290,8 @@ async fn process_listener(source_context: SourceContext, listener_context: liste let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - //let (schedule_check_tx, schedule_check_rx) = mpsc::channel::(100); - //let (unschedule_check_tx, unschedule_check_rx) = mpsc::channel::(100); let mut scheduler = scheduler::CheckScheduler::new(); // todo add shutdown handle - //tokio::task::spawn_local(scheduler.run()); info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); loop { @@ -338,11 +319,14 @@ async fn process_listener(source_context: SourceContext, listener_context: liste info!("Running a check request: {check_request}"); - //schedule_check_tx.send(check_request).await.expect("Couldn't send"); - scheduler.run_check(check_request); + match scheduler.run_check(check_request) { + Ok(_) => {} + Err(e) => { + error!("Error running check: {}", e); + } + } } Some(deleted_entity) = deleted_entities.recv() => { - //unschedule_check_tx.send(deleted_entity).await.expect("Couldn't send"); scheduler.stop_check(deleted_entity); } } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 4aa9184a..03ce9738 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -21,8 +21,7 @@ pub struct CheckScheduler { } impl CheckScheduler { - pub fn new(//schedule_check_rx: mpsc::Receiver, unschedule_check_rx: mpsc::Receiver, - ) -> Self { + pub fn new() -> Self { // todo, add in apis that python checks expect //pyo3::append_to_inittab!(pylib_module); @@ -67,7 +66,7 @@ impl CheckScheduler { let locals = pyo3::types::PyDict::new_bound(py); match py.run_bound(&py_source, None, Some(&locals)) { - Ok(c) => {} + Ok(_) => {} Err(e) => { let traceback = e .traceback_bound(py) @@ -82,7 +81,7 @@ impl CheckScheduler { .unwrap(); let checks = locals .iter() - .filter(|(key, value)| { + .filter(|(_, value)| { if let Ok(class_obj) = value.downcast::() { if class_obj.is(&base_class) { // skip the base class @@ -135,7 +134,7 @@ impl CheckScheduler { let instance = instance.clone(); let check_handle = check_handle.clone(); - let handle = tokio::task::spawn_local(async move { + let handle = tokio::task::spawn(async move { let mut interval = tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); loop { @@ -144,13 +143,21 @@ impl CheckScheduler { info!("Running check instance {idx}"); pyo3::Python::with_gil(|py| { let instance_as_pydict = PyDict::new_bound(py); - instance_as_pydict.set_item("min_collection_interval_ms", instance.min_collection_interval_ms); + instance_as_pydict + .set_item("min_collection_interval_ms", instance.min_collection_interval_ms) + .expect("could not set min-collection-interval"); let instance_list = PyList::new_bound(py, &[instance_as_pydict]); let kwargs = PyDict::new_bound(py); - kwargs.set_item("name", "placeholder_check_name"); - kwargs.set_item("init_config", PyDict::new_bound(py)); // todo this is in the check request maybe - kwargs.set_item("instances", instance_list); + kwargs + .set_item("name", "placeholder_check_name") + .expect("Could not set name"); + kwargs + .set_item("init_config", PyDict::new_bound(py)) + .expect("could not set init_config"); // todo this is in the check request maybe + kwargs + .set_item("instances", instance_list) + .expect("could not set instance list"); let check_ref = &check_handle.id; let pycheck = match check_ref.call_bound(py, (), Some(&kwargs)) { From f60910b59e04a3a9c50d3ba8ebd3409958d89729 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 16 May 2024 20:21:18 +0000 Subject: [PATCH 17/47] Adds initial stubs of some expected native modules --- .../src/sources/checks/aggregator.rs | 39 +++++++++++++ .../src/sources/checks/datadog_agent.rs | 57 +++++++++++++++++++ .../src/sources/checks/mod.rs | 11 +--- .../src/sources/checks/scheduler.rs | 5 +- 4 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 lib/saluki-components/src/sources/checks/aggregator.rs create mode 100644 lib/saluki-components/src/sources/checks/datadog_agent.rs diff --git a/lib/saluki-components/src/sources/checks/aggregator.rs b/lib/saluki-components/src/sources/checks/aggregator.rs new file mode 100644 index 00000000..01e6c7f3 --- /dev/null +++ b/lib/saluki-components/src/sources/checks/aggregator.rs @@ -0,0 +1,39 @@ +use super::*; +use pyo3::prelude::*; + +#[pyfunction] +fn submit_metric(name: String, value: f64, tags: Vec, hostname: String) { + println!( + "submit_metric called with name: {}, value: {}, tags: {:?}, hostname: {}", + name, value, tags, hostname + ); +} + +#[pyfunction] +fn submit_service_check(name: String, status: i32, tags: Vec, hostname: String, message: Option) { + println!( + "submit_service_check called with name: {}, status: {}, tags: {:?}, hostname: {}, message: {:?}", + name, status, tags, hostname, message + ); +} + +#[pyfunction] +fn metrics(name: String) -> Vec { + println!("metrics called for: {}", name); + vec![] // Dummy return +} + +#[pyfunction] +fn reset() { + println!("reset called"); +} + +#[pymodule] +pub fn aggregator(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(submit_metric, m)?)?; + m.add_function(wrap_pyfunction!(submit_service_check, m)?)?; + m.add_function(wrap_pyfunction!(self::metrics, m)?)?; + m.add_function(wrap_pyfunction!(reset, m)?)?; + + Ok(()) +} diff --git a/lib/saluki-components/src/sources/checks/datadog_agent.rs b/lib/saluki-components/src/sources/checks/datadog_agent.rs new file mode 100644 index 00000000..6b6df084 --- /dev/null +++ b/lib/saluki-components/src/sources/checks/datadog_agent.rs @@ -0,0 +1,57 @@ +use super::*; +use pyo3::prelude::*; + +#[pyfunction] +fn get_hostname() -> &'static str { + debug!("Called get_hostname()"); + "stubbed.hostname" +} + +#[pyfunction] +fn set_hostname(hostname: String) { + debug!("Called set_hostname({})", hostname); + // In a function context without a struct, we cannot actually "set" the hostname persistently. +} + +#[pyfunction] +fn reset_hostname() { + debug!("Called reset_hostname()"); + // Similar to `set_hostname`, we cannot reset without a persistent structure. +} + +#[pyfunction] +fn get_config(config_option: String) -> bool { + debug!("Called get_config({})", config_option); + + false +} + +#[pyfunction] +fn get_version() -> &'static str { + debug!("Called get_version()"); + "0.0.0" +} + +#[pyfunction] +fn log(message: String, level: u32) { + debug!("{level} Log: {}", message); +} + +#[pyfunction] +fn set_check_metadata(check_id: String, name: String, value: String) { + debug!("Called set_check_metadata({}, {}, {})", check_id, name, value); + // Again, we can only log this because there's no structure to store it. +} + +#[pymodule] +pub fn datadog_agent(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(get_hostname, m)?)?; + m.add_function(wrap_pyfunction!(set_hostname, m)?)?; + m.add_function(wrap_pyfunction!(reset_hostname, m)?)?; + m.add_function(wrap_pyfunction!(get_config, m)?)?; + m.add_function(wrap_pyfunction!(get_version, m)?)?; + m.add_function(wrap_pyfunction!(log, m)?)?; + m.add_function(wrap_pyfunction!(set_check_metadata, m)?)?; + + Ok(()) +} diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index de90835f..649b841e 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -22,6 +22,8 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; +mod aggregator; +mod datadog_agent; mod listener; mod scheduler; @@ -194,16 +196,9 @@ impl ChecksConfiguration { #[async_trait] impl SourceBuilder for ChecksConfiguration { async fn build(&self) -> Result, GenericError> { - //let (check_run_tx, check_run_rx) = mpsc::channel(100); - //let (check_stop_tx, check_stop_rx) = mpsc::channel(100); - //let runner = CheckRunner::new(check_run_rx, check_stop_rx)?; let listeners = self.build_listeners().await?; - Ok(Box::new(Checks { - listeners, - //check_run_tx, - //check_stop_tx, - })) + Ok(Box::new(Checks { listeners })) } fn outputs(&self) -> &[OutputDefinition] { diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 03ce9738..8eaee17d 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,4 +1,6 @@ use super::*; +use aggregator::aggregator; +use datadog_agent::datadog_agent; use pyo3::prelude::PyAnyMethods; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -23,7 +25,8 @@ pub struct CheckScheduler { impl CheckScheduler { pub fn new() -> Self { // todo, add in apis that python checks expect - //pyo3::append_to_inittab!(pylib_module); + pyo3::append_to_inittab!(datadog_agent); + pyo3::append_to_inittab!(aggregator); pyo3::prepare_freethreaded_python(); From 28feb0dbb191b1fe5d103f8504be37d0fe33a968 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 17 May 2024 14:25:07 +0000 Subject: [PATCH 18/47] small refactor to warm up --- .../src/sources/checks/mod.rs | 1 - .../src/sources/checks/scheduler.rs | 20 +++++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 649b841e..d7ef7860 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -121,7 +121,6 @@ struct YamlCheckInstance { } // checks configuration - #[derive(Debug, Clone, Eq, Hash, PartialEq)] struct CheckInstanceConfiguration { min_collection_interval_ms: u32, diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 8eaee17d..597d5fb4 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -8,13 +8,11 @@ use pyo3::types::PyList; use pyo3::types::PyType; use saluki_error::{generic_error, GenericError}; -struct CheckHandle { - id: Py, -} +struct CheckHandle(Py); impl Clone for CheckHandle { fn clone(&self) -> Self { - Self { id: self.id.clone() } + Self(self.0.clone()) } } @@ -112,9 +110,7 @@ impl CheckScheduler { check_source_path.display(), check_key ); - Ok(CheckHandle { - id: check_value.as_unbound().clone(), - }) + Ok(CheckHandle(check_value.as_unbound().clone())) }) } @@ -162,8 +158,7 @@ impl CheckScheduler { .set_item("instances", instance_list) .expect("could not set instance list"); - let check_ref = &check_handle.id; - let pycheck = match check_ref.call_bound(py, (), Some(&kwargs)) { + let pycheck = match check_handle.0.call_bound(py, (), Some(&kwargs)) { Ok(c) => c, Err(e) => { let traceback = e @@ -186,14 +181,13 @@ impl CheckScheduler { Ok(()) } - pub fn stop_check(&self, check: CheckRequest) { + pub fn stop_check(&mut self, check: CheckRequest) { info!("Deleting check request {check}"); - if let Some((check_handle, running)) = self.running.get(&check.source) { + if let Some((check_handle, running)) = self.running.remove(&check.source) { for handle in running.iter() { handle.abort(); } + drop(check_handle); // release the reference to this check } - // todo delete stuff out of the containers - // and stop/release the class if its not being used } } From 2163a53dab67dbe3a2da2102f7f2524e96c7b7cc Mon Sep 17 00:00:00 2001 From: Remy Mathieu Date: Fri, 17 May 2024 13:42:08 +0200 Subject: [PATCH 19/47] Propagate metrics resulting from a check execution to the Event Buffer. --- Cargo.lock | 8 ++ Cargo.toml | 3 + lib/saluki-components/Cargo.toml | 3 + .../src/sources/checks/aggregator.rs | 129 +++++++++++++++++- .../src/sources/checks/datadog_agent.rs | 7 + .../src/sources/checks/mod.rs | 26 +++- .../src/sources/checks/scheduler.rs | 5 +- 7 files changed, 176 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbc05435..168b6086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2108,6 +2108,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "queues" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0" + [[package]] name = "quote" version = "1.0.36" @@ -2379,11 +2385,13 @@ dependencies = [ "metrics", "metrics-util", "nom", + "once_cell", "paste", "pin-project", "protobuf", "pyo3", "quanta", + "queues", "rustls", "saluki-config", "saluki-core", diff --git a/Cargo.toml b/Cargo.toml index e3e18bc6..cab5a4c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,8 @@ rustls-pemfile = { version = "2", default-features = false } tokio-rustls = { version = "0.25.0", default-features = false } anyhow = { version = "1", default-features = false } chrono = "0.4" +queues = "1.0.2" +once_cell = "1.19.0" [patch.crates-io] # Git dependency for `containerd-client` to add specific `prost-build` settings that allow type-erased payloads in @@ -102,3 +104,4 @@ containerd-client = { git = "https://github.com/containerd/rust-extensions", bra lto = "thin" codegen-units = 4 debug = true + diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 9d0b3eb8..96d380c1 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -49,3 +49,6 @@ tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" pyo3 = { version = "0.21.2", features = ["anyhow"] } +queues = { workspace = true } +once_cell = { workspace = true } + diff --git a/lib/saluki-components/src/sources/checks/aggregator.rs b/lib/saluki-components/src/sources/checks/aggregator.rs index 01e6c7f3..0640ff1e 100644 --- a/lib/saluki-components/src/sources/checks/aggregator.rs +++ b/lib/saluki-components/src/sources/checks/aggregator.rs @@ -1,12 +1,131 @@ use super::*; use pyo3::prelude::*; +use saluki_event::{metric::*, Event}; +use saluki_env::time::get_unix_timestamp; +#[derive(Clone, Copy)] +enum PyMetricType { + Gauge = 0, + Rate, + Count, + MonotonicCount, + Counter, + Histogram, + Historate, +} + +#[derive(Debug, Snafu)] +#[snafu(context(suffix(false)))] +pub enum AggregatorError { + UnsupportedType{}, +} + +impl TryFrom for PyMetricType { + type Error = (); + + fn try_from(v: i32) -> Result { + match v { + x if x == PyMetricType::Gauge as i32 => Ok(PyMetricType::Gauge), + x if x == PyMetricType::Rate as i32 => Ok(PyMetricType::Rate), + x if x == PyMetricType::Count as i32 => Ok(PyMetricType::Count), + x if x == PyMetricType::MonotonicCount as i32 => Ok(PyMetricType::MonotonicCount), + x if x == PyMetricType::Counter as i32 => Ok(PyMetricType::Counter), + x if x == PyMetricType::Histogram as i32 => Ok(PyMetricType::Histogram), + x if x == PyMetricType::Historate as i32 => Ok(PyMetricType::Historate), + _ => Err(()), + } + } +} + +/// CheckMetric are used to transmit metrics from python check execution results +/// to forward in the saluki's pipeline. +pub struct CheckMetric { + name: String, + metric_type: PyMetricType, + value: f64, + tags: Vec, +} + +// TODO(remy): use TryFrom instead +pub fn check_metric_as_event(metric: CheckMetric) -> Result { + let mut tags = MetricTags::default(); + // TODO(remy): do this more idiomatically + for tag in metric.tags.iter() { + tags.insert_tag(tag.clone()); + } + + let context = MetricContext{ + name: metric.name, + tags, + }; + let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); + + match metric.metric_type { + PyMetricType::Gauge => { + Ok(saluki_event::Event::Metric(Metric::from_parts( + context, MetricValue::Gauge { value: metric.value, }, metadata, + ))) + }, + PyMetricType::Counter => { + Ok(saluki_event::Event::Metric(Metric::from_parts( + context, MetricValue::Counter { value: metric.value, }, metadata, + ))) + }, + // TODO(remy): rest of the types + _ => Err(AggregatorError::UnsupportedType{}), + } +} + +impl Clone for CheckMetric { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + metric_type: self.metric_type, + value: self.value.clone(), + tags: self.tags.clone(), + } + } +} + + +/// Global for Python checks execution to report the data +pub static SUBMISSION_QUEUE: Lazy>> = Lazy::new(|| { + Mutex::new(queue![]) +}); + +/// submit_metric is called from the AgentCheck implementation when a check submits a metric. +/// Python signature: +/// aggregator.submit_metric(self, self.check_id, mtype, name, value, tags, hostname, flush_first_value) +/// +/// TODO(remy): should mtype be a PyMetricType? #[pyfunction] -fn submit_metric(name: String, value: f64, tags: Vec, hostname: String) { +fn submit_metric(_class: PyObject, _check_id: String, mtype: i32, name: String, value: f64, tags: Vec, hostname: String, _flush_first_value: bool) { println!( "submit_metric called with name: {}, value: {}, tags: {:?}, hostname: {}", name, value, tags, hostname ); + + let metric_type = match PyMetricType::try_from(mtype) { + Ok(mt) => mt, + Err(e) => { + error!("can't convert metric type: {}", mtype); + PyMetricType::Gauge + } + }; + + let mut q = SUBMISSION_QUEUE.lock().unwrap(); + match q.add(CheckMetric{ + name, + metric_type, + value, + tags, + }) { + Ok(_) => {}, + Err(e) => { + error!("can't push into the submission queue: {}", e); + } + } + drop(q); } #[pyfunction] @@ -35,5 +154,13 @@ pub fn aggregator(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(self::metrics, m)?)?; m.add_function(wrap_pyfunction!(reset, m)?)?; + m.add("GAUGE", PyMetricType::Gauge as i32)?; + m.add("RATE", PyMetricType::Rate as i32)?; + m.add("COUNT", PyMetricType::Count as i32)?; + m.add("MONOTONIC_COUNT", PyMetricType::MonotonicCount as i32)?; + m.add("COUNTER", PyMetricType::Counter as i32)?; + m.add("HISTOGRAM", PyMetricType::Histogram as i32)?; + m.add("HISTORATE", PyMetricType::Historate as i32)?; + Ok(()) } diff --git a/lib/saluki-components/src/sources/checks/datadog_agent.rs b/lib/saluki-components/src/sources/checks/datadog_agent.rs index 6b6df084..d6c635cf 100644 --- a/lib/saluki-components/src/sources/checks/datadog_agent.rs +++ b/lib/saluki-components/src/sources/checks/datadog_agent.rs @@ -43,6 +43,12 @@ fn set_check_metadata(check_id: String, name: String, value: String) { // Again, we can only log this because there's no structure to store it. } +#[pyfunction] +fn tracemalloc_enabled() -> bool { + // tracemalloc unsupported for now + false +} + #[pymodule] pub fn datadog_agent(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_hostname, m)?)?; @@ -52,6 +58,7 @@ pub fn datadog_agent(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_version, m)?)?; m.add_function(wrap_pyfunction!(log, m)?)?; m.add_function(wrap_pyfunction!(set_check_metadata, m)?)?; + m.add_function(wrap_pyfunction!(tracemalloc_enabled, m)?)?; Ok(()) } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index d7ef7860..30995611 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -10,7 +10,7 @@ use saluki_core::{ }, }; use saluki_error::GenericError; -use saluki_event::DataType; +use saluki_event::{DataType, Event}; use serde::Deserialize; use snafu::Snafu; use std::{ @@ -22,6 +22,11 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; +use queues::*; + +use std::sync::Mutex; +use once_cell::sync::Lazy; + mod aggregator; mod datadog_agent; mod listener; @@ -44,7 +49,6 @@ enum Error { reason: String, }, } - /// Checks source. /// /// Scans a directory for check configurations and emits them as things to run. @@ -314,7 +318,23 @@ async fn process_listener(source_context: SourceContext, listener_context: liste info!("Running a check request: {check_request}"); match scheduler.run_check(check_request) { - Ok(_) => {} + Ok(_) => { + // drain the global queue and send everything to source_context + let mut event_buffer = source_context.event_buffer_pool().acquire().await; + + let mut q = aggregator::SUBMISSION_QUEUE.lock().unwrap(); + debug!("will send {} metrics to the event buffer", q.size()); + while q.size() > 0 { + let check_metric = q.remove().expect("can't read from the global submission queue"); + + // TODO(remy): fit the the deserializer/codec architecture instead of doing it here + let event: Event = aggregator::check_metric_as_event(check_metric).expect("can't convert"); + event_buffer.push(event); + debug!("one metric sent to the event buffer from check execution"); + } + debug!("queue drained"); + drop(q); // we're in an infinite loop, explicitely drop q to unlock the mutex + } Err(e) => { error!("Error running check: {}", e); } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 597d5fb4..590426cf 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -171,7 +171,10 @@ impl CheckScheduler { // 'run' method invokes 'check' with the instance we initialized with // ref https://github.com/DataDog/integrations-core/blob/bc3b1c3496e79aa1b75ebcc9ef1c2a2b26487ebd/datadog_checks_base/datadog_checks/base/checks/base.py#L1197 let result = pycheck.call_method0(py, "run").unwrap(); - info!("Result: {:?}", result); + + let s: String = result.extract(py).expect("Can't read the string result from the check execution"); + // TODO(remy): turn this into debug log level later on + info!("Check execution error return: {:?}", s); }) } }); From 642138a07ee097ddf7719b884da806bf128d6977 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 17 May 2024 19:09:30 +0000 Subject: [PATCH 20/47] check metrics are pushed correctly into the event buffer --- .../src/sources/checks/aggregator.rs | 136 +++++++++--------- .../src/sources/checks/mod.rs | 27 ++-- .../src/sources/checks/scheduler.rs | 36 ++++- lib/saluki-event/src/metric/context.rs | 6 + 4 files changed, 115 insertions(+), 90 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/aggregator.rs b/lib/saluki-components/src/sources/checks/aggregator.rs index 0640ff1e..98a5eaf1 100644 --- a/lib/saluki-components/src/sources/checks/aggregator.rs +++ b/lib/saluki-components/src/sources/checks/aggregator.rs @@ -1,10 +1,11 @@ use super::*; use pyo3::prelude::*; -use saluki_event::{metric::*, Event}; use saluki_env::time::get_unix_timestamp; +use saluki_event::{metric::*, Event}; +use tracing::warn; #[derive(Clone, Copy)] -enum PyMetricType { +pub enum PyMetricType { Gauge = 0, Rate, Count, @@ -14,29 +15,30 @@ enum PyMetricType { Historate, } -#[derive(Debug, Snafu)] -#[snafu(context(suffix(false)))] -pub enum AggregatorError { - UnsupportedType{}, -} - -impl TryFrom for PyMetricType { - type Error = (); - - fn try_from(v: i32) -> Result { +impl From for PyMetricType { + fn from(v: i32) -> Self { match v { - x if x == PyMetricType::Gauge as i32 => Ok(PyMetricType::Gauge), - x if x == PyMetricType::Rate as i32 => Ok(PyMetricType::Rate), - x if x == PyMetricType::Count as i32 => Ok(PyMetricType::Count), - x if x == PyMetricType::MonotonicCount as i32 => Ok(PyMetricType::MonotonicCount), - x if x == PyMetricType::Counter as i32 => Ok(PyMetricType::Counter), - x if x == PyMetricType::Histogram as i32 => Ok(PyMetricType::Histogram), - x if x == PyMetricType::Historate as i32 => Ok(PyMetricType::Historate), - _ => Err(()), + 0 => PyMetricType::Gauge, + 1 => PyMetricType::Rate, + 2 => PyMetricType::Count, + 3 => PyMetricType::MonotonicCount, + 4 => PyMetricType::Counter, + 5 => PyMetricType::Histogram, + 6 => PyMetricType::Historate, + _ => { + warn!("Unknown metric type: {}, considering it as a gauge", v); + PyMetricType::Gauge + } } } } +#[derive(Debug, Snafu)] +#[snafu(context(suffix(false)))] +pub enum AggregatorError { + UnsupportedType {}, +} + /// CheckMetric are used to transmit metrics from python check execution results /// to forward in the saluki's pipeline. pub struct CheckMetric { @@ -48,31 +50,27 @@ pub struct CheckMetric { // TODO(remy): use TryFrom instead pub fn check_metric_as_event(metric: CheckMetric) -> Result { - let mut tags = MetricTags::default(); - // TODO(remy): do this more idiomatically - for tag in metric.tags.iter() { - tags.insert_tag(tag.clone()); - } + let tags: MetricTags = metric.tags.into(); - let context = MetricContext{ - name: metric.name, - tags, + let context = MetricContext { + name: metric.name, + tags, }; let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); match metric.metric_type { - PyMetricType::Gauge => { - Ok(saluki_event::Event::Metric(Metric::from_parts( - context, MetricValue::Gauge { value: metric.value, }, metadata, - ))) - }, - PyMetricType::Counter => { - Ok(saluki_event::Event::Metric(Metric::from_parts( - context, MetricValue::Counter { value: metric.value, }, metadata, - ))) - }, + PyMetricType::Gauge => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Gauge { value: metric.value }, + metadata, + ))), + PyMetricType::Counter => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Counter { value: metric.value }, + metadata, + ))), // TODO(remy): rest of the types - _ => Err(AggregatorError::UnsupportedType{}), + _ => Err(AggregatorError::UnsupportedType {}), } } @@ -81,51 +79,53 @@ impl Clone for CheckMetric { Self { name: self.name.clone(), metric_type: self.metric_type, - value: self.value.clone(), + value: self.value, tags: self.tags.clone(), } } } - -/// Global for Python checks execution to report the data -pub static SUBMISSION_QUEUE: Lazy>> = Lazy::new(|| { - Mutex::new(queue![]) -}); - /// submit_metric is called from the AgentCheck implementation when a check submits a metric. /// Python signature: /// aggregator.submit_metric(self, self.check_id, mtype, name, value, tags, hostname, flush_first_value) /// /// TODO(remy): should mtype be a PyMetricType? +#[allow(clippy::too_many_arguments)] #[pyfunction] -fn submit_metric(_class: PyObject, _check_id: String, mtype: i32, name: String, value: f64, tags: Vec, hostname: String, _flush_first_value: bool) { - println!( +#[pyo3(pass_module)] +pub(crate) fn submit_metric( + module: &Bound, _class: PyObject, _check_id: String, mtype: i32, name: String, value: f64, + tags: Vec, hostname: String, _flush_first_value: bool, +) { + debug!( "submit_metric called with name: {}, value: {}, tags: {:?}, hostname: {}", name, value, tags, hostname ); - let metric_type = match PyMetricType::try_from(mtype) { - Ok(mt) => mt, - Err(e) => { - error!("can't convert metric type: {}", mtype); - PyMetricType::Gauge - } - }; - - let mut q = SUBMISSION_QUEUE.lock().unwrap(); - match q.add(CheckMetric{ + let check_metric = CheckMetric { name, - metric_type, + metric_type: mtype.into(), value, tags, - }) { - Ok(_) => {}, + }; + + match module.getattr("SUBMISSION_QUEUE") { + Ok(py_item) => match py_item.extract::>() { + Ok(q) => { + let res = pyo3::Python::with_gil(|py| q.bind_borrowed(py).borrow_mut().sender.clone()); + + match res.try_send(check_metric) { + Ok(_) => debug!("Successfully sent metric"), + Err(e) => error!("Failed to send metric: {}", e), + } + } + Err(e) => error!("Failed to extract SUBMISSION_QUEUE: {}", e), + }, Err(e) => { - error!("can't push into the submission queue: {}", e); + // Theoretically possible early in the init, but not should be basically impossible + error!("SUBMISSION_QUEUE not found: {}", e); } - } - drop(q); + }; } #[pyfunction] @@ -149,10 +149,10 @@ fn reset() { #[pymodule] pub fn aggregator(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(submit_metric, m)?)?; - m.add_function(wrap_pyfunction!(submit_service_check, m)?)?; - m.add_function(wrap_pyfunction!(self::metrics, m)?)?; - m.add_function(wrap_pyfunction!(reset, m)?)?; + m.add_function(wrap_pyfunction_bound!(submit_metric, m)?)?; + m.add_function(wrap_pyfunction_bound!(submit_service_check, m)?)?; + m.add_function(wrap_pyfunction_bound!(self::metrics, m)?)?; + m.add_function(wrap_pyfunction_bound!(reset, m)?)?; m.add("GAUGE", PyMetricType::Gauge as i32)?; m.add("RATE", PyMetricType::Rate as i32)?; diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 30995611..a9d7ec9c 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -24,8 +24,8 @@ use tracing::{debug, error, info}; use queues::*; -use std::sync::Mutex; use once_cell::sync::Lazy; +use std::sync::Mutex; mod aggregator; mod datadog_agent; @@ -288,7 +288,8 @@ async fn process_listener(source_context: SourceContext, listener_context: liste let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - let mut scheduler = scheduler::CheckScheduler::new(); // todo add shutdown handle + let (check_metrics_tx, mut check_metrics_rx) = mpsc::channel(10_000); + let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx); // todo add shutdown handle info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); @@ -306,6 +307,12 @@ async fn process_listener(source_context: SourceContext, listener_context: liste } } } + Some(check_metric) = check_metrics_rx.recv() => { + let mut event_buffer = source_context.event_buffer_pool().acquire().await; + let event: Event = aggregator::check_metric_as_event(check_metric).expect("can't convert"); + debug!("one metric sent to the event buffer from check execution"); + event_buffer.push(event); + } Some(new_entity) = new_entities.recv() => { let check_request = match new_entity.to_runnable_request() { Ok(check_request) => check_request, @@ -319,21 +326,7 @@ async fn process_listener(source_context: SourceContext, listener_context: liste match scheduler.run_check(check_request) { Ok(_) => { - // drain the global queue and send everything to source_context - let mut event_buffer = source_context.event_buffer_pool().acquire().await; - - let mut q = aggregator::SUBMISSION_QUEUE.lock().unwrap(); - debug!("will send {} metrics to the event buffer", q.size()); - while q.size() > 0 { - let check_metric = q.remove().expect("can't read from the global submission queue"); - - // TODO(remy): fit the the deserializer/codec architecture instead of doing it here - let event: Event = aggregator::check_metric_as_event(check_metric).expect("can't convert"); - event_buffer.push(event); - debug!("one metric sent to the event buffer from check execution"); - } - debug!("queue drained"); - drop(q); // we're in an infinite loop, explicitely drop q to unlock the mutex + debug!("Check request succeeded, instances have been queued"); } Err(e) => { error!("Error running check: {}", e); diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 590426cf..76983bc5 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,5 +1,6 @@ +use super::aggregator::aggregator as pyagg; +use super::aggregator::CheckMetric; use super::*; -use aggregator::aggregator; use datadog_agent::datadog_agent; use pyo3::prelude::PyAnyMethods; use pyo3::prelude::*; @@ -16,20 +17,43 @@ impl Clone for CheckHandle { } } +#[pyclass] +pub struct SenderHolder { + pub sender: mpsc::Sender, +} + pub struct CheckScheduler { running: HashMap>)>, } impl CheckScheduler { - pub fn new() -> Self { - // todo, add in apis that python checks expect + pub fn new(send_check_metrics: mpsc::Sender) -> Self { pyo3::append_to_inittab!(datadog_agent); - pyo3::append_to_inittab!(aggregator); + pyo3::append_to_inittab!(pyagg); pyo3::prepare_freethreaded_python(); // Sanity test for python environment before executing pyo3::Python::with_gil(|py| { + match py.import_bound("aggregator") { + Ok(m) => { + let sender_holder = Bound::new( + py, + SenderHolder { + sender: send_check_metrics.clone(), + }, + ) + .expect("Could not create sender holder"); + m.setattr("SUBMISSION_QUEUE", sender_holder) + .expect("Could not set sender_holder on module attribute") + } + Err(e) => { + error!(%e, "Could not import aggregator module."); + if let Some(traceback) = e.traceback_bound(py) { + error!("Traceback: {}", traceback.format().expect("Could format traceback")); + } + } + }; let modd = match py.import_bound("datadog_checks.checks") { Ok(m) => m, Err(e) => { @@ -172,7 +196,9 @@ impl CheckScheduler { // ref https://github.com/DataDog/integrations-core/blob/bc3b1c3496e79aa1b75ebcc9ef1c2a2b26487ebd/datadog_checks_base/datadog_checks/base/checks/base.py#L1197 let result = pycheck.call_method0(py, "run").unwrap(); - let s: String = result.extract(py).expect("Can't read the string result from the check execution"); + let s: String = result + .extract(py) + .expect("Can't read the string result from the check execution"); // TODO(remy): turn this into debug log level later on info!("Check execution error return: {:?}", s); }) diff --git a/lib/saluki-event/src/metric/context.rs b/lib/saluki-event/src/metric/context.rs index 71b786c7..5b1cc3ea 100644 --- a/lib/saluki-event/src/metric/context.rs +++ b/lib/saluki-event/src/metric/context.rs @@ -450,6 +450,12 @@ impl MetricTags { } } +impl From> for MetricTags { + fn from(vec: Vec) -> Self { + Self(vec.into_iter().map(MetricTag::from).collect()) + } +} + impl From for MetricTags { fn from(tag: MetricTag) -> Self { Self(vec![tag]) From e0919152618a598f043650e2e285d35ec432c0b8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 17 May 2024 19:12:38 +0000 Subject: [PATCH 21/47] Removes un-used deps --- Cargo.lock | 8 -------- Cargo.toml | 2 -- lib/saluki-components/Cargo.toml | 2 -- lib/saluki-components/src/sources/checks/mod.rs | 5 ----- 4 files changed, 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 168b6086..dbc05435 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2108,12 +2108,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "queues" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0" - [[package]] name = "quote" version = "1.0.36" @@ -2385,13 +2379,11 @@ dependencies = [ "metrics", "metrics-util", "nom", - "once_cell", "paste", "pin-project", "protobuf", "pyo3", "quanta", - "queues", "rustls", "saluki-config", "saluki-core", diff --git a/Cargo.toml b/Cargo.toml index cab5a4c8..9ab205fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,8 +90,6 @@ rustls-pemfile = { version = "2", default-features = false } tokio-rustls = { version = "0.25.0", default-features = false } anyhow = { version = "1", default-features = false } chrono = "0.4" -queues = "1.0.2" -once_cell = "1.19.0" [patch.crates-io] # Git dependency for `containerd-client` to add specific `prost-build` settings that allow type-erased payloads in diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 96d380c1..3a86e22a 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -49,6 +49,4 @@ tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" pyo3 = { version = "0.21.2", features = ["anyhow"] } -queues = { workspace = true } -once_cell = { workspace = true } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index a9d7ec9c..ee579d3b 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -22,11 +22,6 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; -use queues::*; - -use once_cell::sync::Lazy; -use std::sync::Mutex; - mod aggregator; mod datadog_agent; mod listener; From 122346142cd4e6e3b1c0389cb6c08c323e0b20bf Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 17 May 2024 19:22:57 +0000 Subject: [PATCH 22/47] Forward event buffer with check metrics --- lib/saluki-components/src/sources/checks/mod.rs | 4 +++- lib/saluki-components/src/sources/checks/scheduler.rs | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index ee579d3b..b34a548a 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -305,8 +305,10 @@ async fn process_listener(source_context: SourceContext, listener_context: liste Some(check_metric) = check_metrics_rx.recv() => { let mut event_buffer = source_context.event_buffer_pool().acquire().await; let event: Event = aggregator::check_metric_as_event(check_metric).expect("can't convert"); - debug!("one metric sent to the event buffer from check execution"); event_buffer.push(event); + if let Err(e) = source_context.forwarder().forward(event_buffer).await { + error!(error = %e, "Failed to forward check metrics."); + } } Some(new_entity) = new_entities.recv() => { let check_request = match new_entity.to_runnable_request() { diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 76983bc5..9ba986dd 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -52,6 +52,7 @@ impl CheckScheduler { if let Some(traceback) = e.traceback_bound(py) { error!("Traceback: {}", traceback.format().expect("Could format traceback")); } + return; // fatal } }; let modd = match py.import_bound("datadog_checks.checks") { @@ -61,7 +62,7 @@ impl CheckScheduler { .traceback_bound(py) .expect("Traceback should be present on this error"); error!(%e, "Could not import datadog_checks module. traceback: {}", traceback.format().expect("Could format traceback")); - return; + return; // fatal } }; let class = match modd.getattr("AgentCheck") { @@ -71,7 +72,7 @@ impl CheckScheduler { .traceback_bound(py) .expect("Traceback should be present on this error"); error!(%e, "Could not get AgentCheck class. traceback: {}", traceback.format().expect("Could format traceback")); - return; + return; // fatal } }; info!(%class, %modd, "Was able to import AgentCheck!"); From a4136a21dc1232e96e1e272b70c8e0e973de25b8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 20 May 2024 16:06:16 +0000 Subject: [PATCH 23/47] Move python modules into dedicated file --- .../src/sources/checks/datadog_agent.rs | 64 ------------------- .../src/sources/checks/mod.rs | 14 ++-- ...ggregator.rs => python_exposed_modules.rs} | 62 ++++++++++++++++++ .../src/sources/checks/scheduler.rs | 6 +- 4 files changed, 75 insertions(+), 71 deletions(-) delete mode 100644 lib/saluki-components/src/sources/checks/datadog_agent.rs rename lib/saluki-components/src/sources/checks/{aggregator.rs => python_exposed_modules.rs} (74%) diff --git a/lib/saluki-components/src/sources/checks/datadog_agent.rs b/lib/saluki-components/src/sources/checks/datadog_agent.rs deleted file mode 100644 index d6c635cf..00000000 --- a/lib/saluki-components/src/sources/checks/datadog_agent.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::*; -use pyo3::prelude::*; - -#[pyfunction] -fn get_hostname() -> &'static str { - debug!("Called get_hostname()"); - "stubbed.hostname" -} - -#[pyfunction] -fn set_hostname(hostname: String) { - debug!("Called set_hostname({})", hostname); - // In a function context without a struct, we cannot actually "set" the hostname persistently. -} - -#[pyfunction] -fn reset_hostname() { - debug!("Called reset_hostname()"); - // Similar to `set_hostname`, we cannot reset without a persistent structure. -} - -#[pyfunction] -fn get_config(config_option: String) -> bool { - debug!("Called get_config({})", config_option); - - false -} - -#[pyfunction] -fn get_version() -> &'static str { - debug!("Called get_version()"); - "0.0.0" -} - -#[pyfunction] -fn log(message: String, level: u32) { - debug!("{level} Log: {}", message); -} - -#[pyfunction] -fn set_check_metadata(check_id: String, name: String, value: String) { - debug!("Called set_check_metadata({}, {}, {})", check_id, name, value); - // Again, we can only log this because there's no structure to store it. -} - -#[pyfunction] -fn tracemalloc_enabled() -> bool { - // tracemalloc unsupported for now - false -} - -#[pymodule] -pub fn datadog_agent(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(get_hostname, m)?)?; - m.add_function(wrap_pyfunction!(set_hostname, m)?)?; - m.add_function(wrap_pyfunction!(reset_hostname, m)?)?; - m.add_function(wrap_pyfunction!(get_config, m)?)?; - m.add_function(wrap_pyfunction!(get_version, m)?)?; - m.add_function(wrap_pyfunction!(log, m)?)?; - m.add_function(wrap_pyfunction!(set_check_metadata, m)?)?; - m.add_function(wrap_pyfunction!(tracemalloc_enabled, m)?)?; - - Ok(()) -} diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index b34a548a..f33ab8bd 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -22,9 +22,8 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info}; -mod aggregator; -mod datadog_agent; mod listener; +mod python_exposed_modules; mod scheduler; #[derive(Debug, Snafu)] @@ -79,6 +78,14 @@ impl Display for CheckRequest { impl CheckRequest { fn to_runnable_request(&self) -> Result { + // The concept of a RunnableRequest needs to be expanded + // to look for modules available in the py runtime + // This implies that this step needs to happen later in the process + // Logic to emulate: + // Idea, the RunnableCheckRequest can have an "expected_module_name" field + // or something like that + // That does imply that the `run` of a `runnablecheckrequest` can fail, but that is already the case + // https://github.com/DataDog/datadog-agent/blob/e8de27352093e0d5f828cf86988d186a3501b525/pkg/collector/python/loader.go#L110-L112 let check_source_code = match &self.source { CheckSource::Yaml(path) => find_sibling_py_file(path)?, }; @@ -135,7 +142,6 @@ enum CheckSource { } impl CheckSource { - // todo refactor to async fs fn to_check_request(&self) -> Result { match self { CheckSource::Yaml(path) => { @@ -304,7 +310,7 @@ async fn process_listener(source_context: SourceContext, listener_context: liste } Some(check_metric) = check_metrics_rx.recv() => { let mut event_buffer = source_context.event_buffer_pool().acquire().await; - let event: Event = aggregator::check_metric_as_event(check_metric).expect("can't convert"); + let event: Event = python_exposed_modules::check_metric_as_event(check_metric).expect("can't convert"); event_buffer.push(event); if let Err(e) = source_context.forwarder().forward(event_buffer).await { error!(error = %e, "Failed to forward check metrics."); diff --git a/lib/saluki-components/src/sources/checks/aggregator.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs similarity index 74% rename from lib/saluki-components/src/sources/checks/aggregator.rs rename to lib/saluki-components/src/sources/checks/python_exposed_modules.rs index 98a5eaf1..d7961cf5 100644 --- a/lib/saluki-components/src/sources/checks/aggregator.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -164,3 +164,65 @@ pub fn aggregator(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } + +#[pyfunction] +fn get_hostname() -> &'static str { + debug!("Called get_hostname()"); + "stubbed.hostname" +} + +#[pyfunction] +fn set_hostname(hostname: String) { + debug!("Called set_hostname({})", hostname); + // In a function context without a struct, we cannot actually "set" the hostname persistently. +} + +#[pyfunction] +fn reset_hostname() { + debug!("Called reset_hostname()"); + // Similar to `set_hostname`, we cannot reset without a persistent structure. +} + +#[pyfunction] +fn get_config(config_option: String) -> bool { + debug!("Called get_config({})", config_option); + + false +} + +#[pyfunction] +fn get_version() -> &'static str { + debug!("Called get_version()"); + "0.0.0" +} + +#[pyfunction] +fn log(message: String, level: u32) { + debug!("{level} Log: {}", message); +} + +#[pyfunction] +fn set_check_metadata(check_id: String, name: String, value: String) { + debug!("Called set_check_metadata({}, {}, {})", check_id, name, value); + // Again, we can only log this because there's no structure to store it. +} + +#[pyfunction] +fn tracemalloc_enabled() -> bool { + // tracemalloc unsupported for now + false +} + +#[pymodule] +pub fn datadog_agent(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction!(get_hostname, m)?)?; + m.add_function(wrap_pyfunction!(set_hostname, m)?)?; + m.add_function(wrap_pyfunction!(reset_hostname, m)?)?; + m.add_function(wrap_pyfunction!(get_config, m)?)?; + m.add_function(wrap_pyfunction!(get_version, m)?)?; + m.add_function(wrap_pyfunction!(log, m)?)?; + m.add_function(wrap_pyfunction!(set_check_metadata, m)?)?; + m.add_function(wrap_pyfunction!(tracemalloc_enabled, m)?)?; + + Ok(()) +} diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 9ba986dd..022775b8 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,7 +1,7 @@ -use super::aggregator::aggregator as pyagg; -use super::aggregator::CheckMetric; +use super::python_exposed_modules::aggregator as pyagg; +use super::python_exposed_modules::datadog_agent; +use super::python_exposed_modules::CheckMetric; use super::*; -use datadog_agent::datadog_agent; use pyo3::prelude::PyAnyMethods; use pyo3::prelude::*; use pyo3::types::PyDict; From e2574547d28121cabe815e92fec9d0c361de3ba1 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 20 May 2024 16:10:16 +0000 Subject: [PATCH 24/47] Refactor conversion into TryInto --- .../src/sources/checks/mod.rs | 2 +- .../sources/checks/python_exposed_modules.rs | 46 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index f33ab8bd..20248730 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -310,7 +310,7 @@ async fn process_listener(source_context: SourceContext, listener_context: liste } Some(check_metric) = check_metrics_rx.recv() => { let mut event_buffer = source_context.event_buffer_pool().acquire().await; - let event: Event = python_exposed_modules::check_metric_as_event(check_metric).expect("can't convert"); + let event: Event = check_metric.try_into().expect("can't convert"); event_buffer.push(event); if let Err(e) = source_context.forwarder().forward(event_buffer).await { error!(error = %e, "Failed to forward check metrics."); diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index d7961cf5..1f234212 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -48,29 +48,29 @@ pub struct CheckMetric { tags: Vec, } -// TODO(remy): use TryFrom instead -pub fn check_metric_as_event(metric: CheckMetric) -> Result { - let tags: MetricTags = metric.tags.into(); - - let context = MetricContext { - name: metric.name, - tags, - }; - let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); - - match metric.metric_type { - PyMetricType::Gauge => Ok(saluki_event::Event::Metric(Metric::from_parts( - context, - MetricValue::Gauge { value: metric.value }, - metadata, - ))), - PyMetricType::Counter => Ok(saluki_event::Event::Metric(Metric::from_parts( - context, - MetricValue::Counter { value: metric.value }, - metadata, - ))), - // TODO(remy): rest of the types - _ => Err(AggregatorError::UnsupportedType {}), +impl TryInto for CheckMetric { + type Error = AggregatorError; + + fn try_into(self) -> Result { + let tags: MetricTags = self.tags.into(); + + let context = MetricContext { name: self.name, tags }; + let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); + + match self.metric_type { + PyMetricType::Gauge => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Gauge { value: self.value }, + metadata, + ))), + PyMetricType::Counter => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Counter { value: self.value }, + metadata, + ))), + // TODO(remy): rest of the types + _ => Err(AggregatorError::UnsupportedType {}), + } } } From 3c61c01dc948f8919569f3644d17ace1bddadb3a Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 20 May 2024 16:22:52 +0000 Subject: [PATCH 25/47] Some cleanup --- .../src/sources/checks/mod.rs | 92 ++++++++++++++++++- .../sources/checks/python_exposed_modules.rs | 88 +----------------- .../src/sources/checks/scheduler.rs | 12 ++- 3 files changed, 97 insertions(+), 95 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 20248730..abb5d406 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -9,8 +9,9 @@ use saluki_core::{ OutputDefinition, }, }; +use saluki_env::time::get_unix_timestamp; use saluki_error::GenericError; -use saluki_event::{DataType, Event}; +use saluki_event::{metric::*, DataType, Event}; use serde::Deserialize; use snafu::Snafu; use std::{ @@ -20,7 +21,7 @@ use std::{ use std::{collections::HashSet, io}; use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; -use tracing::{debug, error, info}; +use tracing::{debug, error, info, warn}; mod listener; mod python_exposed_modules; @@ -289,8 +290,10 @@ async fn process_listener(source_context: SourceContext, listener_context: liste let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - let (check_metrics_tx, mut check_metrics_rx) = mpsc::channel(10_000); - let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx); // todo add shutdown handle + // Note: This model has a single CheckScheduler per listener + // which is likely not the design we want long term + let (check_metrics_tx, mut check_metrics_rx) = mpsc::channel(10_000_000); + let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx); info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); @@ -370,3 +373,84 @@ fn find_sibling_py_file(check_yaml_path: &Path) -> Result { // TODO(remy): look for `check_name.d` directory instead. Ok(check_rel_filepath) } + +#[derive(Clone, Copy)] +pub enum PyMetricType { + Gauge = 0, + Rate, + Count, + MonotonicCount, + Counter, + Histogram, + Historate, +} + +impl From for PyMetricType { + fn from(v: i32) -> Self { + match v { + 0 => PyMetricType::Gauge, + 1 => PyMetricType::Rate, + 2 => PyMetricType::Count, + 3 => PyMetricType::MonotonicCount, + 4 => PyMetricType::Counter, + 5 => PyMetricType::Histogram, + 6 => PyMetricType::Historate, + _ => { + warn!("Unknown metric type: {}, considering it as a gauge", v); + PyMetricType::Gauge + } + } + } +} + +#[derive(Debug, Snafu)] +#[snafu(context(suffix(false)))] +pub enum AggregatorError { + UnsupportedType {}, +} + +/// CheckMetric are used to transmit metrics from python check execution results +/// to forward in the saluki's pipeline. +pub struct CheckMetric { + name: String, + metric_type: PyMetricType, + value: f64, + tags: Vec, +} + +impl TryInto for CheckMetric { + type Error = AggregatorError; + + fn try_into(self) -> Result { + let tags: MetricTags = self.tags.into(); + + let context = MetricContext { name: self.name, tags }; + let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); + + match self.metric_type { + PyMetricType::Gauge => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Gauge { value: self.value }, + metadata, + ))), + PyMetricType::Counter => Ok(saluki_event::Event::Metric(Metric::from_parts( + context, + MetricValue::Counter { value: self.value }, + metadata, + ))), + // TODO(remy): rest of the types + _ => Err(AggregatorError::UnsupportedType {}), + } + } +} + +impl Clone for CheckMetric { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + metric_type: self.metric_type, + value: self.value, + tags: self.tags.clone(), + } + } +} diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index 1f234212..ac1d6c7e 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -1,89 +1,5 @@ use super::*; use pyo3::prelude::*; -use saluki_env::time::get_unix_timestamp; -use saluki_event::{metric::*, Event}; -use tracing::warn; - -#[derive(Clone, Copy)] -pub enum PyMetricType { - Gauge = 0, - Rate, - Count, - MonotonicCount, - Counter, - Histogram, - Historate, -} - -impl From for PyMetricType { - fn from(v: i32) -> Self { - match v { - 0 => PyMetricType::Gauge, - 1 => PyMetricType::Rate, - 2 => PyMetricType::Count, - 3 => PyMetricType::MonotonicCount, - 4 => PyMetricType::Counter, - 5 => PyMetricType::Histogram, - 6 => PyMetricType::Historate, - _ => { - warn!("Unknown metric type: {}, considering it as a gauge", v); - PyMetricType::Gauge - } - } - } -} - -#[derive(Debug, Snafu)] -#[snafu(context(suffix(false)))] -pub enum AggregatorError { - UnsupportedType {}, -} - -/// CheckMetric are used to transmit metrics from python check execution results -/// to forward in the saluki's pipeline. -pub struct CheckMetric { - name: String, - metric_type: PyMetricType, - value: f64, - tags: Vec, -} - -impl TryInto for CheckMetric { - type Error = AggregatorError; - - fn try_into(self) -> Result { - let tags: MetricTags = self.tags.into(); - - let context = MetricContext { name: self.name, tags }; - let metadata = MetricMetadata::from_timestamp(get_unix_timestamp()); - - match self.metric_type { - PyMetricType::Gauge => Ok(saluki_event::Event::Metric(Metric::from_parts( - context, - MetricValue::Gauge { value: self.value }, - metadata, - ))), - PyMetricType::Counter => Ok(saluki_event::Event::Metric(Metric::from_parts( - context, - MetricValue::Counter { value: self.value }, - metadata, - ))), - // TODO(remy): rest of the types - _ => Err(AggregatorError::UnsupportedType {}), - } - } -} - -impl Clone for CheckMetric { - fn clone(&self) -> Self { - Self { - name: self.name.clone(), - metric_type: self.metric_type, - value: self.value, - tags: self.tags.clone(), - } - } -} /// submit_metric is called from the AgentCheck implementation when a check submits a metric. /// Python signature: @@ -122,8 +38,8 @@ pub(crate) fn submit_metric( Err(e) => error!("Failed to extract SUBMISSION_QUEUE: {}", e), }, Err(e) => { - // Theoretically possible early in the init, but not should be basically impossible - error!("SUBMISSION_QUEUE not found: {}", e); + // This is a fatal error and should be impossible to hit this + unreachable!("SUBMISSION_QUEUE not found: {}", e); } }; } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 022775b8..94b40cfc 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,6 +1,5 @@ use super::python_exposed_modules::aggregator as pyagg; use super::python_exposed_modules::datadog_agent; -use super::python_exposed_modules::CheckMetric; use super::*; use pyo3::prelude::PyAnyMethods; use pyo3::prelude::*; @@ -33,8 +32,8 @@ impl CheckScheduler { pyo3::prepare_freethreaded_python(); - // Sanity test for python environment before executing pyo3::Python::with_gil(|py| { + // Initialize the aggregator module with the submission queue match py.import_bound("aggregator") { Ok(m) => { let sender_holder = Bound::new( @@ -55,6 +54,9 @@ impl CheckScheduler { return; // fatal } }; + + // Test to ensure expected module is available + // fail early, it is fatal if these are not present let modd = match py.import_bound("datadog_checks.checks") { Ok(m) => m, Err(e) => { @@ -83,8 +85,8 @@ impl CheckScheduler { } } - // compiles the python source code and instantiates it (??) into the VM - // returns an opaque handle to the check + // See `CheckRequest::to_runnable_request` for more TODO items here + // Returns an opaque handle to the python class implementing the class fn register_check(&mut self, check_source_path: PathBuf) -> Result { let py_source = std::fs::read_to_string(&check_source_path).unwrap(); @@ -201,7 +203,7 @@ impl CheckScheduler { .extract(py) .expect("Can't read the string result from the check execution"); // TODO(remy): turn this into debug log level later on - info!("Check execution error return: {:?}", s); + debug!("Check execution returned {:?}", s); }) } }); From ba05534b64b3c2c2c63f3061b8e6a230533a9fa4 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 20 May 2024 19:15:45 +0000 Subject: [PATCH 26/47] Slightly changes expected python loading structure to more closely align with Agent --- .gitignore | 2 +- README.md | 2 +- conf.d/simple.py | 5 - dist/checks.d/simple.py | 10 ++ {conf.d => dist/conf.d}/simple.yaml | 0 .../src/sources/checks/mod.rs | 65 +++++---- .../src/sources/checks/scheduler.rs | 132 ++++++++++++++---- 7 files changed, 153 insertions(+), 63 deletions(-) delete mode 100644 conf.d/simple.py create mode 100644 dist/checks.d/simple.py rename {conf.d => dist/conf.d}/simple.yaml (100%) diff --git a/.gitignore b/.gitignore index 53b9cd32..1a6e4ebc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ target/ .vscode/ tooling/bin/ test/k8s/charts/ -test/build/ \ No newline at end of file +test/build/dist/checks.d/__pycache__ diff --git a/README.md b/README.md index ac0ed1e6..0a886f40 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ All remaining crates are part of Saluki itself, and all have a name with the pre ### Saluki Components #### Checks -Experimental support for python checks is present if there is a local `./conf.d` +Experimental support for python checks is present if there is a local `./dist/conf.d` folder present with a valid python check and yaml config file with the same name. diff --git a/conf.d/simple.py b/conf.d/simple.py deleted file mode 100644 index 6431e494..00000000 --- a/conf.d/simple.py +++ /dev/null @@ -1,5 +0,0 @@ -from datadog_checks.checks import AgentCheck - -class ChurnCheck(AgentCheck): - def check(self, instance): - self.gauge('computed_value', 42, tags=['hello:world']) diff --git a/dist/checks.d/simple.py b/dist/checks.d/simple.py new file mode 100644 index 00000000..12b73f10 --- /dev/null +++ b/dist/checks.d/simple.py @@ -0,0 +1,10 @@ +from datadog_checks.checks import AgentCheck + +class SimpleCheck(AgentCheck): + def __init__(self, name, init_config, instances): + super(SimpleCheck, self).__init__(name, init_config, instances) + print("Init config: {}".format(init_config)) + + + def check(self, instance): + self.gauge('computed_value', 42, tags=['hello:world', 'argument:{}'.format(instance.get('argument'))]) diff --git a/conf.d/simple.yaml b/dist/conf.d/simple.yaml similarity index 100% rename from conf.d/simple.yaml rename to dist/conf.d/simple.yaml diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index abb5d406..79a0837d 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -55,7 +55,7 @@ pub struct ChecksConfiguration { } fn default_check_config_dir() -> String { - "./conf.d".to_string() + "./dist/conf.d".to_string() } #[derive(Debug, Clone)] @@ -87,11 +87,17 @@ impl CheckRequest { // or something like that // That does imply that the `run` of a `runnablecheckrequest` can fail, but that is already the case // https://github.com/DataDog/datadog-agent/blob/e8de27352093e0d5f828cf86988d186a3501b525/pkg/collector/python/loader.go#L110-L112 - let check_source_code = match &self.source { - CheckSource::Yaml(path) => find_sibling_py_file(path)?, + let name = if let Some(name) = &self.name { + name.clone() + } else { + self.source.to_check_name()? + }; + let check_source_code: Option = match &self.source { + CheckSource::Yaml(path) => find_sibling_py_file(path), }; Ok(RunnableCheckRequest { check_request: self.clone(), + check_name: name, check_source_code, }) } @@ -99,17 +105,13 @@ impl CheckRequest { struct RunnableCheckRequest { check_request: CheckRequest, - check_source_code: PathBuf, + check_name: String, + check_source_code: Option, } impl Display for RunnableCheckRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Request: {} CheckSource: {}", - self.check_request, - self.check_source_code.display() - ) + write!(f, "Request: {} CheckSource: {}", self.check_request, self.check_name) } } @@ -143,6 +145,17 @@ enum CheckSource { } impl CheckSource { + fn to_check_name(&self) -> Result { + match self { + CheckSource::Yaml(path) => { + let mut path = path.clone(); + path.set_extension(""); // trim off the file extension + let filename = path.file_name().expect("No error"); + let name = filename.to_string_lossy().to_string(); + Ok(name) + } + } + } fn to_check_request(&self) -> Result { match self { CheckSource::Yaml(path) => { @@ -281,7 +294,9 @@ impl Source for Checks { } } -async fn process_listener(source_context: SourceContext, listener_context: listener::DirCheckListenerContext) { +async fn process_listener( + source_context: SourceContext, listener_context: listener::DirCheckListenerContext, +) -> Result<(), GenericError> { let listener::DirCheckListenerContext { shutdown_handle, mut listener, @@ -290,10 +305,10 @@ async fn process_listener(source_context: SourceContext, listener_context: liste let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - // Note: This model has a single CheckScheduler per listener + // Note: This architecture has a single CheckScheduler per listener // which is likely not the design we want long term let (check_metrics_tx, mut check_metrics_rx) = mpsc::channel(10_000_000); - let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx); + let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx)?; info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); @@ -348,30 +363,20 @@ async fn process_listener(source_context: SourceContext, listener_context: liste stream_shutdown_coordinator.shutdown().await; info!("Check listener stopped."); + + Ok(()) } /// Given a yaml config, find the corresponding python source code /// Currently only looks in the same directory, no support for `checks.d` or `mycheck.d` directories -fn find_sibling_py_file(check_yaml_path: &Path) -> Result { +fn find_sibling_py_file(check_yaml_path: &Path) -> Option { let mut check_rel_filepath = check_yaml_path.to_path_buf(); check_rel_filepath.set_extension("py"); + if check_rel_filepath.exists() { + return Some(check_rel_filepath); + } - // let filename = check_rel_filepath.file_name().unwrap(); // TODO(remy): what about None here? - - // if !check_rel_filepath.pop() { - // return Err(Error::NoSourceAvailable{ reason: format!("Can't go to parent directory") }); - // } - - // check_rel_filepath.push(filename); - - // if !check_rel_filepath.exists() { - // check in a checks.d subdir - // check_rel_filepath.push("checks.d"); - // return Err(Error::NoSourceAvailable{ reason: format!("c") }); // TODO(remy): ship the rel filepath in the error - // } - - // TODO(remy): look for `check_name.d` directory instead. - Ok(check_rel_filepath) + None } #[derive(Clone, Copy)] diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 94b40cfc..64bc39af 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,3 +1,5 @@ +use std::fs; + use super::python_exposed_modules::aggregator as pyagg; use super::python_exposed_modules::datadog_agent; use super::*; @@ -23,16 +25,27 @@ pub struct SenderHolder { pub struct CheckScheduler { running: HashMap>)>, + agent_check_base_class: Py, } impl CheckScheduler { - pub fn new(send_check_metrics: mpsc::Sender) -> Self { + pub fn new(send_check_metrics: mpsc::Sender) -> Result { pyo3::append_to_inittab!(datadog_agent); pyo3::append_to_inittab!(pyagg); pyo3::prepare_freethreaded_python(); - pyo3::Python::with_gil(|py| { + let mut agent_check_base_class = None; + + pyo3::Python::with_gil(|py| -> Result<(), GenericError> { + let syspath: &PyList = py.import_bound("sys")?.getattr("path")?.extract()?; + // Mimicing the python path setup from the Agent + // https://github.com/DataDog/datadog-agent/blob/b039ea43d3168f521e8ea3e8356a0e84eec170d1/cmd/agent/common/common.go#L24-L33 + syspath.insert(0, Path::new("./dist/"))?; // path.GetDistPath() + syspath.insert(0, Path::new("./dist/checks.d/"))?; // custom checks in checks.d subdir + // syspath.insert(0, config.get('additional_checksd'))?; // config option not supported yet + println!("Import path is: {:?}", syspath); + // Initialize the aggregator module with the submission queue match py.import_bound("aggregator") { Ok(m) => { @@ -51,7 +64,7 @@ impl CheckScheduler { if let Some(traceback) = e.traceback_bound(py) { error!("Traceback: {}", traceback.format().expect("Could format traceback")); } - return; // fatal + return Err(generic_error!("Could not import 'aggregator' module")); } }; @@ -64,32 +77,101 @@ impl CheckScheduler { .traceback_bound(py) .expect("Traceback should be present on this error"); error!(%e, "Could not import datadog_checks module. traceback: {}", traceback.format().expect("Could format traceback")); - return; // fatal + return Err(generic_error!("Could not import 'datadog_checks' module")); } }; - let class = match modd.getattr("AgentCheck") { - Ok(c) => c, + match modd.getattr("AgentCheck") { + Ok(c) => { + agent_check_base_class = Some(c.unbind()); + } Err(e) => { let traceback = e .traceback_bound(py) .expect("Traceback should be present on this error"); error!(%e, "Could not get AgentCheck class. traceback: {}", traceback.format().expect("Could format traceback")); - return; // fatal + return Err(generic_error!("Could not find 'AgentCheck' class")); } }; - info!(%class, %modd, "Was able to import AgentCheck!"); - }); + info!("Found all pre-requisites for Agent python check execution"); + + Ok(()) + })?; - Self { + Ok(Self { running: HashMap::new(), - } + agent_check_base_class: agent_check_base_class.expect("AgentCheck class should be present"), + }) } // See `CheckRequest::to_runnable_request` for more TODO items here - // Returns an opaque handle to the python class implementing the class - fn register_check(&mut self, check_source_path: PathBuf) -> Result { - let py_source = std::fs::read_to_string(&check_source_path).unwrap(); + // Returns an opaque handle to the python class implementing the check + fn register_check(&mut self, check: &RunnableCheckRequest) -> Result { + let check_name = &check.check_name; + // if there is a specific source, then this will populate into locals and can be found + if let Some(py_source_path) = &check.check_source_code { + let py_source = fs::read_to_string(py_source_path) + .map_err(|e| generic_error!("Could not read check source file: {}", e))?; + return self.register_check_with_source(py_source); + } + // if there is no specific source, then we need to + // import module + // findSubclassOf(AgentCheck, module) + // - use Dir to find all items in module + // - Get item + // - Check if it is a subclass of AgentCheck + for import_str in &[&check.check_name, &format!("datadog_checks.{}", check.check_name)] { + match self.register_check_from_imports(import_str) { + Ok(handle) => return Ok(handle), + Err(e) => { + error!(%e, "Could not find check {check_name} from imports"); + } + } + } + Err(generic_error!("Could not find class for check {check_name}")) + } + + fn register_check_from_imports(&mut self, import_path: &str) -> Result { + pyo3::Python::with_gil(|py| { + debug!("Imported '{import_path}'"); + let module = py.import_bound(import_path)?; + let base_class = self.agent_check_base_class.bind(py); + + let checks = module + .dict() + .iter() + .filter(|(name, value)| { + debug!( + "Found a thing: {value}, with type {t}", + t = value.get_type().name().unwrap_or(std::borrow::Cow::Borrowed("unknown")) + ); + let class_bound: &Bound = match value.downcast() { + Ok(c) => c, + Err(_) => return false, + }; + debug!("Found a class: {class_bound}"); + if class_bound.is(base_class) { + // skip the base class + return false; + } + if let Ok(true) = class_bound.is_subclass(base_class) { + return true; + } + false + }) + .collect::>(); + if checks.is_empty() { + return Err(generic_error!("No checks found in source")); + } + if checks.len() >= 2 { + return Err(generic_error!("Multiple checks found in source")); + } + let (check_key, check_value) = &checks[0]; + info!("Found base class {check_key} for check {import_path} {check_value:?}"); + Ok(CheckHandle(check_value.as_unbound().clone())) + }) + } + fn register_check_with_source(&mut self, py_source: String) -> Result { pyo3::Python::with_gil(|py| { let locals = pyo3::types::PyDict::new_bound(py); @@ -103,19 +185,16 @@ impl CheckScheduler { return Err(generic_error!("Could not compile check source")); } }; - let base_class = locals - .get_item("AgentCheck") - .expect("Could not get 'AgentCheck' class") - .unwrap(); + let base_class = self.agent_check_base_class.bind(py); let checks = locals .iter() .filter(|(_, value)| { if let Ok(class_obj) = value.downcast::() { - if class_obj.is(&base_class) { + if class_obj.is(base_class) { // skip the base class return false; } - if let Ok(true) = class_obj.is_subclass(&base_class) { + if let Ok(true) = class_obj.is_subclass(base_class) { return true; } false @@ -132,11 +211,7 @@ impl CheckScheduler { return Err(generic_error!("Multiple checks found in source")); } let (check_key, check_value) = &checks[0]; - info!( - "For check source {}, found check: {}", - check_source_path.display(), - check_key - ); + info!("Found check {check_key} from source: {py_source}"); Ok(CheckHandle(check_value.as_unbound().clone())) }) } @@ -150,7 +225,12 @@ impl CheckScheduler { // registry should probably queue off of checkhandle //let current = self.running.entry(check.check_request.source.clone()).or_default(); - let check_handle = self.register_check(check.check_source_code.clone())?; + let check_handle = self.register_check(&check)?; + // TODO there may be an 'init' step needed after loading the check + // See `rtloader/three/three.cpp::getCheck` for calls: + // - `AgentCheck.load_config(init_config)` + // - `AgentCheck.load_config(instance)` + // and set attr 'check_id' equal to the check id let running_entry = self .running .entry(check.check_request.source) From 8d5c2f8aa9ebbada9ee5b1f64405f5e76687c3e8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Mon, 20 May 2024 20:47:02 +0000 Subject: [PATCH 27/47] small fixes --- .gitignore | 3 +- .../src/sources/checks/scheduler.rs | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 1a6e4ebc..3c6ce1ef 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target/ .vscode/ tooling/bin/ test/k8s/charts/ -test/build/dist/checks.d/__pycache__ +test/build +dist/checks.d/__pycache__ diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 64bc39af..fef56102 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -68,8 +68,7 @@ impl CheckScheduler { } }; - // Test to ensure expected module is available - // fail early, it is fatal if these are not present + // Validate that python env is correctly configured let modd = match py.import_bound("datadog_checks.checks") { Ok(m) => m, Err(e) => { @@ -106,6 +105,23 @@ impl CheckScheduler { // See `CheckRequest::to_runnable_request` for more TODO items here // Returns an opaque handle to the python class implementing the check fn register_check(&mut self, check: &RunnableCheckRequest) -> Result { + let check_handle = match self.register_check_impl(check) { + Ok(h) => h, + Err(e) => { + error!(%e, "Could not register check {}", check.check_name); + return Err(e); + } + }; + // TODO there may be an 'init' step needed after loading the check + // See `rtloader/three/three.cpp::getCheck` for calls: + // - `AgentCheck.load_config(init_config)` + // - `AgentCheck.load_config(instance)` + // and set attr 'check_id' equal to the check id + + Ok(check_handle) + } + + fn register_check_impl(&mut self, check: &RunnableCheckRequest) -> Result { let check_name = &check.check_name; // if there is a specific source, then this will populate into locals and can be found if let Some(py_source_path) = &check.check_source_code { @@ -113,12 +129,6 @@ impl CheckScheduler { .map_err(|e| generic_error!("Could not read check source file: {}", e))?; return self.register_check_with_source(py_source); } - // if there is no specific source, then we need to - // import module - // findSubclassOf(AgentCheck, module) - // - use Dir to find all items in module - // - Get item - // - Check if it is a subclass of AgentCheck for import_str in &[&check.check_name, &format!("datadog_checks.{}", check.check_name)] { match self.register_check_from_imports(import_str) { Ok(handle) => return Ok(handle), @@ -132,7 +142,7 @@ impl CheckScheduler { fn register_check_from_imports(&mut self, import_path: &str) -> Result { pyo3::Python::with_gil(|py| { - debug!("Imported '{import_path}'"); + debug!("Imported '{import_path}', checking its exports for subclasses of 'AgentCheck'"); let module = py.import_bound(import_path)?; let base_class = self.agent_check_base_class.bind(py); @@ -141,7 +151,7 @@ impl CheckScheduler { .iter() .filter(|(name, value)| { debug!( - "Found a thing: {value}, with type {t}", + "Found {name}: {value}, with type {t}", t = value.get_type().name().unwrap_or(std::borrow::Cow::Borrowed("unknown")) ); let class_bound: &Bound = match value.downcast() { @@ -173,6 +183,7 @@ impl CheckScheduler { fn register_check_with_source(&mut self, py_source: String) -> Result { pyo3::Python::with_gil(|py| { + debug!("Running provided check source and checking locals for subclasses of 'AgentCheck'"); let locals = pyo3::types::PyDict::new_bound(py); match py.run_bound(&py_source, None, Some(&locals)) { @@ -226,11 +237,6 @@ impl CheckScheduler { //let current = self.running.entry(check.check_request.source.clone()).or_default(); let check_handle = self.register_check(&check)?; - // TODO there may be an 'init' step needed after loading the check - // See `rtloader/three/three.cpp::getCheck` for calls: - // - `AgentCheck.load_config(init_config)` - // - `AgentCheck.load_config(instance)` - // and set attr 'check_id' equal to the check id let running_entry = self .running .entry(check.check_request.source) From ebd14b1e9d00acac50b0b5cb7369272522129311 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 17:54:52 +0000 Subject: [PATCH 28/47] Adds instructions for loading venv with required agent check libs --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0a886f40..869eb07a 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,23 @@ All remaining crates are part of Saluki itself, and all have a name with the pre ### Saluki Components #### Checks -Experimental support for python checks is present if there is a local `./dist/conf.d` -folder present with a valid python check and yaml config file with the same -name. +Python checks can be loaded and executed from the active venv via yaml config files in `./dist/conf.d`. -pyo3 is used to provide python support, which works off of your system's python -install, `sudo apt install libpython3-devel` (todo double check this) +Local python checks can be placed in `./dist/foo.py` and configured via `./dist/conf.d/foo.yaml`. -a venv is present that must have the following installed: -- `pip3 install datadog_checks_base` -- `pip3 install datadog_checks_base[deps]` +pyo3 is used to provide python support, which works off of your system's python +install, `sudo apt install libpython3-devel` (todo double check the package name) + +todo update instructions with full integrations-core package installation +``` +#pip install datadog_checks_base # maybe not needed bc its a dependency +#pip install datadog_checks_base[deps] # maybe not needed bc its a dependency +cp $HOME/dev/integrations-core/requirements-agent-release.txt . +pip install -r $(awk -v local_path_base="$HOME/dev/integrations-core" '{sub(/^datadog-/, "", $1); gsub(/-/, "_", $1); split($1, parts, "=="); package_name = parts[1]; if (index(package_name, "checks_") != 1) {local_path = local_path_base "/" package_name "/"; print local_path}}' requirements-agent-release.txt) +# One error +# ERROR: Package 'datadog-tokumx' requires a different Python: 3.10.12 not in '==2.7.*' +# Not bad. +``` ## Contributing From ea36274b07701943bdeee64ab9cc0e6805b0b508 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 17:55:34 +0000 Subject: [PATCH 29/47] Parse yaml instance config and pass it through as native py objects --- dist/conf.d/http_check.yaml | 6 ++ .../src/sources/checks/mod.rs | 81 +++++++++++++++---- .../sources/checks/python_exposed_modules.rs | 1 - .../src/sources/checks/scheduler.rs | 18 ++--- 4 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 dist/conf.d/http_check.yaml diff --git a/dist/conf.d/http_check.yaml b/dist/conf.d/http_check.yaml new file mode 100644 index 00000000..b11654ec --- /dev/null +++ b/dist/conf.d/http_check.yaml @@ -0,0 +1,6 @@ +init_config: + ca_certs: "/etc/ssl/certs/ca-certificates.crt" + +instances: + - name: datadog + url: https://datadoghq.com/ \ No newline at end of file diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 79a0837d..d3c158be 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use async_walkdir::{DirEntry, Filtering, WalkDir}; use futures::StreamExt as _; +use pyo3::prelude::*; use saluki_config::GenericConfiguration; use saluki_core::{ components::{Source, SourceBuilder, SourceContext}, @@ -10,7 +11,7 @@ use saluki_core::{ }, }; use saluki_env::time::get_unix_timestamp; -use saluki_error::GenericError; +use saluki_error::{generic_error, GenericError}; use saluki_event::{metric::*, DataType, Event}; use serde::Deserialize; use snafu::Snafu; @@ -119,20 +120,66 @@ impl Display for RunnableCheckRequest { struct YamlCheckConfiguration { name: Option, init_config: Option, - instances: Vec, + instances: Vec, } -#[derive(Debug, Deserialize)] -struct YamlCheckInstance { - #[serde(default = "default_min_collection_interval_ms")] - min_collection_interval: u32, - // todo support arbitrary other fields +#[derive(Debug, Clone)] +struct CheckInstanceConfiguration(HashMap); + +impl CheckInstanceConfiguration { + fn min_collection_interval_ms(&self) -> u32 { + self.0 + .get("min_collection_interval") + .map(|v| v.as_i64().expect("min_collection_interval must be an integer") as u32) + .unwrap_or_else(default_min_collection_interval_ms) + } + + fn into_pydict<'a, 'py>(&'a self, p: &'py pyo3::Python) -> Bound<'py, pyo3::types::PyDict> { + let dict = pyo3::types::PyDict::new_bound(*p); + + for (key, value) in &self.0 { + let value = serde_value_to_pytype(value, p).expect("Could convert"); + dict.set_item(key, value).expect("can't add"); + } + dict + } } -// checks configuration -#[derive(Debug, Clone, Eq, Hash, PartialEq)] -struct CheckInstanceConfiguration { - min_collection_interval_ms: u32, +// TODO finish this +// the return type may need to be a generic idk +fn serde_value_to_pytype<'py>(value: &serde_yaml::Value, p: &'py pyo3::Python) -> PyResult> { + match value { + serde_yaml::Value::String(s) => Ok(s.into_py(*p).into_bound(*p)), + serde_yaml::Value::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(i.into_py(*p).into_bound(*p)) + } else if let Some(f) = n.as_f64() { + Ok(f.into_py(*p).into_bound(*p)) + } else { + unreachable!("Number is neither i64 nor f64") + } + } + serde_yaml::Value::Bool(b) => Ok(b.into_py(*p).into_bound(*p)), + serde_yaml::Value::Sequence(s) => { + let list = pyo3::types::PyList::empty_bound(*p); + for item in s { + let item = serde_value_to_pytype(item, p)?; + list.append(item)?; + } + Ok(list.into_any()) + } + serde_yaml::Value::Mapping(m) => { + let dict = pyo3::types::PyDict::new_bound(*p); + for (key, value) in m { + let value = serde_value_to_pytype(value, p)?; + let key = serde_value_to_pytype(key, p)?; + dict.set_item(key, value)?; + } + Ok(dict.into_any()) + } + serde_yaml::Value::Null => Ok(pyo3::types::PyNone::get_bound(*p).to_owned().into_any()), + serde_yaml::Value::Tagged(_) => Err(generic_error!("Tagged values are not supported").into()), + } } fn default_min_collection_interval_ms() -> u32 { @@ -170,9 +217,15 @@ impl CheckSource { }; for instance in read_yaml.instances.into_iter() { - checks_config.push(CheckInstanceConfiguration { - min_collection_interval_ms: instance.min_collection_interval, - }); + let mapping: serde_yaml::Mapping = + instance.as_mapping().expect("Only mapping instance configs").to_owned(); + + let map: HashMap = mapping + .into_iter() + .map(|(k, v)| (k.as_str().expect("Only string instance config keys").to_string(), v)) + .collect(); + + checks_config.push(CheckInstanceConfiguration(map)); } Ok(CheckRequest { diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index ac1d6c7e..37c410f2 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -1,5 +1,4 @@ use super::*; -use pyo3::prelude::*; /// submit_metric is called from the AgentCheck implementation when a check submits a metric. /// Python signature: diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index fef56102..6296ae5a 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -4,7 +4,6 @@ use super::python_exposed_modules::aggregator as pyagg; use super::python_exposed_modules::datadog_agent; use super::*; use pyo3::prelude::PyAnyMethods; -use pyo3::prelude::*; use pyo3::types::PyDict; use pyo3::types::PyList; use pyo3::types::PyType; @@ -112,11 +111,15 @@ impl CheckScheduler { return Err(e); } }; - // TODO there may be an 'init' step needed after loading the check + // TODO What 'init' is needed after grabbing a handle to the check class? // See `rtloader/three/three.cpp::getCheck` for calls: // - `AgentCheck.load_config(init_config)` // - `AgentCheck.load_config(instance)` - // and set attr 'check_id' equal to the check id + + // JK load_config is just yaml parsing -- str -> pyAny + // which I don't need because I implemented serde_mapping -> pydict + + // - set attr 'check_id' equal to the check id Ok(check_handle) } @@ -248,16 +251,13 @@ impl CheckScheduler { let check_handle = check_handle.clone(); let handle = tokio::task::spawn(async move { let mut interval = - tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms.into())); + tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms().into())); loop { interval.tick().await; // run check info!("Running check instance {idx}"); pyo3::Python::with_gil(|py| { - let instance_as_pydict = PyDict::new_bound(py); - instance_as_pydict - .set_item("min_collection_interval_ms", instance.min_collection_interval_ms) - .expect("could not set min-collection-interval"); + let instance_as_pydict = instance.into_pydict(&py); let instance_list = PyList::new_bound(py, &[instance_as_pydict]); let kwargs = PyDict::new_bound(py); @@ -288,7 +288,7 @@ impl CheckScheduler { let s: String = result .extract(py) .expect("Can't read the string result from the check execution"); - // TODO(remy): turn this into debug log level later on + debug!("Check execution returned {:?}", s); }) } From 4c640bb5c5bc57901febc87fbbdd3b51739d1c6a Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 18:16:03 +0000 Subject: [PATCH 30/47] Adds support for init_config and instance config --- .../src/sources/checks/mod.rs | 53 ++++++++++++++----- .../src/sources/checks/scheduler.rs | 10 ++-- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index d3c158be..6fc13e70 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -63,7 +63,7 @@ fn default_check_config_dir() -> String { struct CheckRequest { name: Option, instances: Vec, - init_config: Option, + init_config: CheckInitConfiguration, source: CheckSource, } @@ -119,8 +119,28 @@ impl Display for RunnableCheckRequest { #[derive(Debug, Deserialize)] struct YamlCheckConfiguration { name: Option, - init_config: Option, - instances: Vec, + init_config: Option, + instances: Vec, +} + +fn map_to_pydict<'py>( + map: &HashMap, p: &'py pyo3::Python, +) -> PyResult> { + let dict = pyo3::types::PyDict::new_bound(*p); + for (key, value) in map { + let value = serde_value_to_pytype(value, p)?; + dict.set_item(key, value)?; + } + Ok(dict) +} + +#[derive(Debug, Clone)] +struct CheckInitConfiguration(HashMap); + +impl CheckInitConfiguration { + fn to_pydict<'py>(&self, p: &'py pyo3::Python) -> Bound<'py, pyo3::types::PyDict> { + map_to_pydict(&self.0, p).expect("Could convert") + } } #[derive(Debug, Clone)] @@ -134,14 +154,8 @@ impl CheckInstanceConfiguration { .unwrap_or_else(default_min_collection_interval_ms) } - fn into_pydict<'a, 'py>(&'a self, p: &'py pyo3::Python) -> Bound<'py, pyo3::types::PyDict> { - let dict = pyo3::types::PyDict::new_bound(*p); - - for (key, value) in &self.0 { - let value = serde_value_to_pytype(value, p).expect("Could convert"); - dict.set_item(key, value).expect("can't add"); - } - dict + fn to_pydict<'py>(&self, p: &'py pyo3::Python) -> Bound<'py, pyo3::types::PyDict> { + map_to_pydict(&self.0, p).expect("Could convert") } } @@ -215,10 +229,21 @@ impl CheckSource { return Err(Error::CantReadConfiguration { source: e }); } }; + let init_config = if let Some(init_config) = read_yaml.init_config { + let mapping: serde_yaml::Mapping = init_config; + + let map: HashMap = mapping + .into_iter() + .map(|(k, v)| (k.as_str().expect("Only string instance config keys").to_string(), v)) + .collect(); + + CheckInitConfiguration(map) + } else { + CheckInitConfiguration(HashMap::new()) + }; for instance in read_yaml.instances.into_iter() { - let mapping: serde_yaml::Mapping = - instance.as_mapping().expect("Only mapping instance configs").to_owned(); + let mapping: serde_yaml::Mapping = instance.to_owned(); let map: HashMap = mapping .into_iter() @@ -231,7 +256,7 @@ impl CheckSource { Ok(CheckRequest { name: read_yaml.name, instances: checks_config, - init_config: read_yaml.init_config, + init_config, source: self.clone(), }) } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 6296ae5a..c0fa51de 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -8,6 +8,7 @@ use pyo3::types::PyDict; use pyo3::types::PyList; use pyo3::types::PyType; use saluki_error::{generic_error, GenericError}; +use tracing::trace; struct CheckHandle(Py); @@ -153,7 +154,7 @@ impl CheckScheduler { .dict() .iter() .filter(|(name, value)| { - debug!( + trace!( "Found {name}: {value}, with type {t}", t = value.get_type().name().unwrap_or(std::borrow::Cow::Borrowed("unknown")) ); @@ -161,7 +162,7 @@ impl CheckScheduler { Ok(c) => c, Err(_) => return false, }; - debug!("Found a class: {class_bound}"); + trace!("Found a class: {class_bound}"); if class_bound.is(base_class) { // skip the base class return false; @@ -247,6 +248,7 @@ impl CheckScheduler { for (idx, instance) in check.check_request.instances.iter().enumerate() { let instance = instance.clone(); + let init_config = check.check_request.init_config.clone(); let check_handle = check_handle.clone(); let handle = tokio::task::spawn(async move { @@ -257,7 +259,7 @@ impl CheckScheduler { // run check info!("Running check instance {idx}"); pyo3::Python::with_gil(|py| { - let instance_as_pydict = instance.into_pydict(&py); + let instance_as_pydict = instance.to_pydict(&py); let instance_list = PyList::new_bound(py, &[instance_as_pydict]); let kwargs = PyDict::new_bound(py); @@ -265,7 +267,7 @@ impl CheckScheduler { .set_item("name", "placeholder_check_name") .expect("Could not set name"); kwargs - .set_item("init_config", PyDict::new_bound(py)) + .set_item("init_config", init_config.to_pydict(&py)) .expect("could not set init_config"); // todo this is in the check request maybe kwargs .set_item("instances", instance_list) From 5bb0be56c5c290542f40ac532dc7b4e50dff09a7 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 18:51:19 +0000 Subject: [PATCH 31/47] Refactor in pursuit of functional unit tests --- .../src/sources/checks/listener.rs | 8 +- .../src/sources/checks/mod.rs | 70 ++++-------- .../src/sources/checks/scheduler.rs | 101 +++++++++++++++++- 3 files changed, 124 insertions(+), 55 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index 88a7cb88..7f0f1938 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -91,7 +91,13 @@ impl DirCheckRequestListener { entries .filter_map(|e| async move { match e { - Ok(entry) => Some(CheckSource::Yaml(entry.path().to_path_buf())), + Ok(entry) => match tokio::fs::read_to_string(entry.path()).await { + Ok(content) => Some(CheckSource::Yaml((entry.path(), content))), + Err(e) => { + eprintln!("Error reading file: {}", e); + None + } + }, Err(e) => { eprintln!("Error traversing files: {}", e); None diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 6fc13e70..65932c34 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -62,6 +62,7 @@ fn default_check_config_dir() -> String { #[derive(Debug, Clone)] struct CheckRequest { name: Option, + module_name: String, instances: Vec, init_config: CheckInitConfiguration, source: CheckSource, @@ -80,39 +81,23 @@ impl Display for CheckRequest { impl CheckRequest { fn to_runnable_request(&self) -> Result { - // The concept of a RunnableRequest needs to be expanded - // to look for modules available in the py runtime - // This implies that this step needs to happen later in the process - // Logic to emulate: - // Idea, the RunnableCheckRequest can have an "expected_module_name" field - // or something like that - // That does imply that the `run` of a `runnablecheckrequest` can fail, but that is already the case - // https://github.com/DataDog/datadog-agent/blob/e8de27352093e0d5f828cf86988d186a3501b525/pkg/collector/python/loader.go#L110-L112 - let name = if let Some(name) = &self.name { - name.clone() - } else { - self.source.to_check_name()? - }; - let check_source_code: Option = match &self.source { - CheckSource::Yaml(path) => find_sibling_py_file(path), - }; Ok(RunnableCheckRequest { check_request: self.clone(), - check_name: name, - check_source_code, + module_name: self.module_name.clone(), + check_source_code: None, }) } } struct RunnableCheckRequest { check_request: CheckRequest, - check_name: String, + module_name: String, check_source_code: Option, } impl Display for RunnableCheckRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Request: {} CheckSource: {}", self.check_request, self.check_name) + write!(f, "Request: {} CheckSource: {}", self.check_request, self.module_name) } } @@ -134,7 +119,7 @@ fn map_to_pydict<'py>( Ok(dict) } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] struct CheckInitConfiguration(HashMap); impl CheckInitConfiguration { @@ -202,30 +187,18 @@ fn default_min_collection_interval_ms() -> u32 { #[derive(Debug, Clone, Eq, Hash, PartialEq)] enum CheckSource { - Yaml(PathBuf), + Yaml((PathBuf, String)), } impl CheckSource { - fn to_check_name(&self) -> Result { - match self { - CheckSource::Yaml(path) => { - let mut path = path.clone(); - path.set_extension(""); // trim off the file extension - let filename = path.file_name().expect("No error"); - let name = filename.to_string_lossy().to_string(); - Ok(name) - } - } - } fn to_check_request(&self) -> Result { match self { - CheckSource::Yaml(path) => { - let file = std::fs::File::open(path).expect("No error"); + CheckSource::Yaml((path, contents)) => { let mut checks_config = Vec::new(); - let read_yaml: YamlCheckConfiguration = match serde_yaml::from_reader(file) { + let read_yaml: YamlCheckConfiguration = match serde_yaml::from_str(contents) { Ok(read) => read, Err(e) => { - debug!("can't read configuration at {}: {}", path.display(), e); + error!(%e, "Can't decode yaml as check configuration: {contents}"); return Err(Error::CantReadConfiguration { source: e }); } }; @@ -253,8 +226,14 @@ impl CheckSource { checks_config.push(CheckInstanceConfiguration(map)); } + let module_name = path + .file_stem() + .expect("File name must have a stem") + .to_string_lossy() + .to_string(); Ok(CheckRequest { name: read_yaml.name, + module_name, instances: checks_config, init_config, source: self.clone(), @@ -267,7 +246,7 @@ impl CheckSource { impl Display for CheckSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - CheckSource::Yaml(path) => write!(f, "{}", path.display()), + CheckSource::Yaml((path, contents)) => write!(f, "{contents} from file: {}", path.display()), } } } @@ -445,19 +424,7 @@ async fn process_listener( Ok(()) } -/// Given a yaml config, find the corresponding python source code -/// Currently only looks in the same directory, no support for `checks.d` or `mycheck.d` directories -fn find_sibling_py_file(check_yaml_path: &Path) -> Option { - let mut check_rel_filepath = check_yaml_path.to_path_buf(); - check_rel_filepath.set_extension("py"); - if check_rel_filepath.exists() { - return Some(check_rel_filepath); - } - - None -} - -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum PyMetricType { Gauge = 0, Rate, @@ -494,6 +461,7 @@ pub enum AggregatorError { /// CheckMetric are used to transmit metrics from python check execution results /// to forward in the saluki's pipeline. +#[derive(Debug, PartialEq)] pub struct CheckMetric { name: String, metric_type: PyMetricType, diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index c0fa51de..52bb5c77 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -108,7 +108,7 @@ impl CheckScheduler { let check_handle = match self.register_check_impl(check) { Ok(h) => h, Err(e) => { - error!(%e, "Could not register check {}", check.check_name); + error!(%e, "Could not register check {}", check.module_name); return Err(e); } }; @@ -126,14 +126,14 @@ impl CheckScheduler { } fn register_check_impl(&mut self, check: &RunnableCheckRequest) -> Result { - let check_name = &check.check_name; + let check_name = &check.module_name; // if there is a specific source, then this will populate into locals and can be found if let Some(py_source_path) = &check.check_source_code { let py_source = fs::read_to_string(py_source_path) .map_err(|e| generic_error!("Could not read check source file: {}", e))?; return self.register_check_with_source(py_source); } - for import_str in &[&check.check_name, &format!("datadog_checks.{}", check.check_name)] { + for import_str in &[&check.module_name, &format!("datadog_checks.{}", check.module_name)] { match self.register_check_from_imports(import_str) { Ok(handle) => return Ok(handle), Err(e) => { @@ -311,3 +311,98 @@ impl CheckScheduler { } } } + +#[cfg(test)] +mod tests { + use super::super::*; + use super::*; + use tokio::sync::mpsc; + + #[tokio::test] + async fn test_new_check_scheduler() { + let (sender, _) = mpsc::channel(10); + let scheduler = CheckScheduler::new(sender).unwrap(); + assert!(scheduler.running.is_empty()); + } + + #[tokio::test] + async fn test_register_check_with_source() { + let (sender, _) = mpsc::channel(10); + let mut scheduler = CheckScheduler::new(sender).unwrap(); + let py_source = r#" + class MyCheck(AgentCheck): + def check(self, instance): + pass + "#; + let check_handle = scheduler.register_check_with_source(py_source.to_string()).unwrap(); + assert_eq!(scheduler.running.len(), 1); + pyo3::Python::with_gil(|py| { + let check_class_ref = check_handle.0.bind(py); + assert!(check_class_ref.is_callable()); + }); + } + + #[tokio::test] + async fn test_run_check() { + let (sender, mut receiver) = mpsc::channel(10); + let mut scheduler = CheckScheduler::new(sender).unwrap(); + let py_source = r#" + class MyCheck(AgentCheck): + def check(self, instance): + self.gauge('test-metric-name', 41, tags=['hello:world']) + pass + "#; + let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: []".to_string())); + let check_request = CheckRequest { + name: None, + module_name: "my_check".to_string(), + source: source.clone(), + instances: vec![], + init_config: CheckInitConfiguration::default(), + }; + let runnable_check_request = RunnableCheckRequest { + module_name: "my_check".to_string(), + check_source_code: None, // put literal py code in here + check_request, + }; + scheduler.run_check(runnable_check_request).unwrap(); + assert_eq!(scheduler.running.len(), 1); + assert_eq!(scheduler.running.keys().next(), Some(&source)); + + // Simulate receiving a check metric + let check_metric = CheckMetric { + name: "test-metric-name".to_string(), + metric_type: PyMetricType::Gauge, + value: 41.0, + tags: vec!["hello:world".to_string()], + }; + let check_from_channel = receiver.recv().await.unwrap(); + assert_eq!(check_from_channel, check_metric); + } + + /* + #[tokio::test] + async fn test_stop_check() { + let (sender, _) = mpsc::channel(10); + let mut scheduler = CheckScheduler::new(sender).unwrap(); + let check_request = CheckRequest { + module_name: "my_check".to_string(), + name: Some("MyCheck".to_string()), + source: CheckSource::Yaml("my_check.py".to_string()), + instances: vec![], + init_config: CheckInitConfiguration::default(), + }; + let runnable_check_request = RunnableCheckRequest { + module_name: "my_check".to_string(), + check_source_code: None, + check_request, + }; + + scheduler.run_check(runnable_check_request).unwrap(); + assert_eq!(scheduler.running.len(), 1); + + scheduler.stop_check(check_request).unwrap(); + assert!(scheduler.running.is_empty()); + } + */ +} From 4d8f264b8f324b4c5034b701b16111b3622976d8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 20:36:47 +0000 Subject: [PATCH 32/47] Test coverage!! --- Cargo.lock | 82 ++++++++--- lib/saluki-components/Cargo.toml | 1 + .../src/sources/checks/mod.rs | 6 +- .../src/sources/checks/scheduler.rs | 135 +++++++++--------- 4 files changed, 130 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbc05435..c6d01d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,7 +150,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -167,7 +167,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -320,7 +320,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.61", "which", ] @@ -343,7 +343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9990737a6d5740ff51cdbbc0f0503015cb30c390f6623968281eb214a520cfc0" dependencies = [ "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -805,7 +805,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1741,7 +1741,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1791,7 +1791,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1832,7 +1832,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -1883,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.61", ] [[package]] @@ -1903,7 +1903,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", "version_check", "yansi", ] @@ -1951,7 +1951,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 2.0.61", "tempfile", ] @@ -1965,7 +1965,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2077,7 +2077,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2090,7 +2090,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2399,6 +2399,7 @@ dependencies = [ "tokio-util", "tower", "tracing", + "tracing-test", "url", ] @@ -2620,7 +2621,7 @@ checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2735,7 +2736,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2760,6 +2761,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.61" @@ -2812,7 +2824,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2909,7 +2921,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -2985,7 +2997,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -3059,7 +3071,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] @@ -3094,6 +3106,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", + "smallvec", "thread_local", "time", "tracing", @@ -3101,6 +3114,29 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote", + "syn 1.0.109", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -3244,7 +3280,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -3266,7 +3302,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3492,7 +3528,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.61", ] [[package]] diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 3a86e22a..628d2f6f 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -49,4 +49,5 @@ tracing = { workspace = true } url = { workspace = true } async-walkdir = "1.0.0" pyo3 = { version = "0.21.2", features = ["anyhow"] } +tracing-test = "0.2.4" diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 65932c34..8b17cdc8 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -83,7 +83,6 @@ impl CheckRequest { fn to_runnable_request(&self) -> Result { Ok(RunnableCheckRequest { check_request: self.clone(), - module_name: self.module_name.clone(), check_source_code: None, }) } @@ -91,13 +90,12 @@ impl CheckRequest { struct RunnableCheckRequest { check_request: CheckRequest, - module_name: String, - check_source_code: Option, + check_source_code: Option, } impl Display for RunnableCheckRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Request: {} CheckSource: {}", self.check_request, self.module_name) + write!(f, "Request: {}", self.check_request) } } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 52bb5c77..9b821d92 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -1,5 +1,3 @@ -use std::fs; - use super::python_exposed_modules::aggregator as pyagg; use super::python_exposed_modules::datadog_agent; use super::*; @@ -44,7 +42,7 @@ impl CheckScheduler { syspath.insert(0, Path::new("./dist/"))?; // path.GetDistPath() syspath.insert(0, Path::new("./dist/checks.d/"))?; // custom checks in checks.d subdir // syspath.insert(0, config.get('additional_checksd'))?; // config option not supported yet - println!("Import path is: {:?}", syspath); + debug!("Python sys.path is: {:?}", syspath); // Initialize the aggregator module with the submission queue match py.import_bound("aggregator") { @@ -72,10 +70,10 @@ impl CheckScheduler { let modd = match py.import_bound("datadog_checks.checks") { Ok(m) => m, Err(e) => { - let traceback = e - .traceback_bound(py) - .expect("Traceback should be present on this error"); - error!(%e, "Could not import datadog_checks module. traceback: {}", traceback.format().expect("Could format traceback")); + error!(%e, "Could not import datadog_checks module"); + if let Some(traceback) = e.traceback_bound(py) { + error!("Traceback: {}", traceback.format().expect("Could format traceback")); + } return Err(generic_error!("Could not import 'datadog_checks' module")); } }; @@ -84,10 +82,10 @@ impl CheckScheduler { agent_check_base_class = Some(c.unbind()); } Err(e) => { - let traceback = e - .traceback_bound(py) - .expect("Traceback should be present on this error"); - error!(%e, "Could not get AgentCheck class. traceback: {}", traceback.format().expect("Could format traceback")); + error!(%e, "Could not get AgentCheck class."); + if let Some(traceback) = e.traceback_bound(py) { + error!("Traceback: {}", traceback.format().expect("Could format traceback")); + } return Err(generic_error!("Could not find 'AgentCheck' class")); } }; @@ -108,7 +106,7 @@ impl CheckScheduler { let check_handle = match self.register_check_impl(check) { Ok(h) => h, Err(e) => { - error!(%e, "Could not register check {}", check.module_name); + error!(%e, "Could not register check {}", check.check_request.module_name); return Err(e); } }; @@ -126,22 +124,20 @@ impl CheckScheduler { } fn register_check_impl(&mut self, check: &RunnableCheckRequest) -> Result { - let check_name = &check.module_name; + let check_module_name = &check.check_request.module_name; // if there is a specific source, then this will populate into locals and can be found - if let Some(py_source_path) = &check.check_source_code { - let py_source = fs::read_to_string(py_source_path) - .map_err(|e| generic_error!("Could not read check source file: {}", e))?; - return self.register_check_with_source(py_source); + if let Some(py_source) = &check.check_source_code { + return self.register_check_with_source(py_source.clone()); } - for import_str in &[&check.module_name, &format!("datadog_checks.{}", check.module_name)] { + for import_str in &[&check_module_name, &&format!("datadog_checks.{}", check_module_name)] { match self.register_check_from_imports(import_str) { Ok(handle) => return Ok(handle), Err(e) => { - error!(%e, "Could not find check {check_name} from imports"); + error!(%e, "Could not find check {check_module_name} from imports"); } } } - Err(generic_error!("Could not find class for check {check_name}")) + Err(generic_error!("Could not find class for check {check_module_name}")) } fn register_check_from_imports(&mut self, import_path: &str) -> Result { @@ -193,10 +189,10 @@ impl CheckScheduler { match py.run_bound(&py_source, None, Some(&locals)) { Ok(_) => {} Err(e) => { - let traceback = e - .traceback_bound(py) - .expect("Traceback should be present on this error"); - error!(%e, "Could not compile check source. traceback: {}", traceback.format().expect("Could format traceback")); + error!(%e, "Could not compile check source"); + if let Some(traceback) = e.traceback_bound(py) { + error!("Traceback: {}", traceback.format().expect("Could format traceback")); + } return Err(generic_error!("Could not compile check source")); } }; @@ -237,20 +233,23 @@ impl CheckScheduler { // queues a run of the check every min_collection_interval_ms // 3. Stores the handles in the running hashmap pub fn run_check(&mut self, check: RunnableCheckRequest) -> Result<(), GenericError> { - // registry should probably queue off of checkhandle - //let current = self.running.entry(check.check_request.source.clone()).or_default(); - let check_handle = self.register_check(&check)?; let running_entry = self .running .entry(check.check_request.source) .or_insert((check_handle.clone(), Vec::new())); + info!( + "Running check {mname} with {num_instances} instances", + mname = check.check_request.module_name, + num_instances = check.check_request.instances.len() + ); for (idx, instance) in check.check_request.instances.iter().enumerate() { let instance = instance.clone(); let init_config = check.check_request.init_config.clone(); let check_handle = check_handle.clone(); + debug!("Spawning task for check instance {idx}"); let handle = tokio::task::spawn(async move { let mut interval = tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms().into())); @@ -268,7 +267,7 @@ impl CheckScheduler { .expect("Could not set name"); kwargs .set_item("init_config", init_config.to_pydict(&py)) - .expect("could not set init_config"); // todo this is in the check request maybe + .expect("could not set init_config"); kwargs .set_item("instances", instance_list) .expect("could not set instance list"); @@ -276,10 +275,10 @@ impl CheckScheduler { let pycheck = match check_handle.0.call_bound(py, (), Some(&kwargs)) { Ok(c) => c, Err(e) => { - let traceback = e - .traceback_bound(py) - .expect("Traceback should be present on this error"); - error!(%e, "Could not instantiate check. traceback: {}", traceback.format().expect("Could format traceback")); + error!(%e, "Could not instantiate check."); + if let Some(traceback) = e.traceback_bound(py) { + error!("Traceback: {}", traceback.format().expect("Could format traceback")); + } return; } }; @@ -317,6 +316,7 @@ mod tests { use super::super::*; use super::*; use tokio::sync::mpsc; + use tracing_test::traced_test; #[tokio::test] async fn test_new_check_scheduler() { @@ -330,46 +330,42 @@ mod tests { let (sender, _) = mpsc::channel(10); let mut scheduler = CheckScheduler::new(sender).unwrap(); let py_source = r#" - class MyCheck(AgentCheck): - def check(self, instance): - pass +from datadog_checks.checks import AgentCheck + +class MyCheck(AgentCheck): + def check(self, instance): + pass "#; let check_handle = scheduler.register_check_with_source(py_source.to_string()).unwrap(); - assert_eq!(scheduler.running.len(), 1); pyo3::Python::with_gil(|py| { let check_class_ref = check_handle.0.bind(py); assert!(check_class_ref.is_callable()); }); } - #[tokio::test] + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_run_check() { let (sender, mut receiver) = mpsc::channel(10); let mut scheduler = CheckScheduler::new(sender).unwrap(); let py_source = r#" - class MyCheck(AgentCheck): - def check(self, instance): - self.gauge('test-metric-name', 41, tags=['hello:world']) - pass - "#; - let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: []".to_string())); - let check_request = CheckRequest { - name: None, - module_name: "my_check".to_string(), - source: source.clone(), - instances: vec![], - init_config: CheckInitConfiguration::default(), - }; +from datadog_checks.checks import AgentCheck + +class MyCheck(AgentCheck): + def check(self, instance): + self.gauge('test-metric-name', 41, tags=['hello:world'])"#; + + let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let check_request = source.to_check_request().unwrap(); + let runnable_check_request = RunnableCheckRequest { - module_name: "my_check".to_string(), - check_source_code: None, // put literal py code in here + check_source_code: Some(py_source.to_string()), check_request, }; + scheduler.run_check(runnable_check_request).unwrap(); assert_eq!(scheduler.running.len(), 1); assert_eq!(scheduler.running.keys().next(), Some(&source)); - // Simulate receiving a check metric let check_metric = CheckMetric { name: "test-metric-name".to_string(), metric_type: PyMetricType::Gauge, @@ -380,29 +376,34 @@ mod tests { assert_eq!(check_from_channel, check_metric); } - /* #[tokio::test] async fn test_stop_check() { - let (sender, _) = mpsc::channel(10); + let (sender, mut receiver) = mpsc::channel(10); let mut scheduler = CheckScheduler::new(sender).unwrap(); - let check_request = CheckRequest { - module_name: "my_check".to_string(), - name: Some("MyCheck".to_string()), - source: CheckSource::Yaml("my_check.py".to_string()), - instances: vec![], - init_config: CheckInitConfiguration::default(), - }; + let py_source = r#" +from datadog_checks.checks import AgentCheck + +class MyCheck(AgentCheck): + def check(self, instance): + self.gauge('test-metric-name', 41, tags=['hello:world'])"#; + + let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let check_request = source.to_check_request().unwrap(); + let runnable_check_request = RunnableCheckRequest { - module_name: "my_check".to_string(), - check_source_code: None, - check_request, + check_source_code: Some(py_source.to_string()), + check_request: check_request.clone(), }; scheduler.run_check(runnable_check_request).unwrap(); assert_eq!(scheduler.running.len(), 1); + assert_eq!(scheduler.running.keys().next(), Some(&source)); - scheduler.stop_check(check_request).unwrap(); + scheduler.stop_check(check_request); assert!(scheduler.running.is_empty()); + + receiver + .try_recv() + .expect_err("No check metrics should be received after stopping the check"); } - */ } From d922752f31c14ebda51917df77150cb4a6aadb90 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 20:52:46 +0000 Subject: [PATCH 33/47] implement memory bounds for checks --- lib/saluki-components/src/sources/checks/mod.rs | 10 ++++++++-- lib/saluki-components/src/sources/checks/scheduler.rs | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 8b17cdc8..7b6e13b6 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use async_walkdir::{DirEntry, Filtering, WalkDir}; use futures::StreamExt as _; +use memory_accounting::{MemoryBounds, MemoryBoundsBuilder}; use pyo3::prelude::*; use saluki_config::GenericConfiguration; use saluki_core::{ @@ -142,8 +143,6 @@ impl CheckInstanceConfiguration { } } -// TODO finish this -// the return type may need to be a generic idk fn serde_value_to_pytype<'py>(value: &serde_yaml::Value, p: &'py pyo3::Python) -> PyResult> { match value { serde_yaml::Value::String(s) => Ok(s.into_py(*p).into_bound(*p)), @@ -281,6 +280,13 @@ impl SourceBuilder for ChecksConfiguration { } } +impl MemoryBounds for ChecksConfiguration { + fn specify_bounds(&self, builder: &mut MemoryBoundsBuilder) { + // We allocate nothing up front + builder.minimum().with_fixed_amount(0); + } +} + pub struct Checks { listeners: Vec, } diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index 9b821d92..c08098b4 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -316,7 +316,6 @@ mod tests { use super::super::*; use super::*; use tokio::sync::mpsc; - use tracing_test::traced_test; #[tokio::test] async fn test_new_check_scheduler() { From 69bc1b3de94889479bd97129cc436133d44ed245 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Tue, 21 May 2024 21:06:44 +0000 Subject: [PATCH 34/47] Maybe fix CI for pyo3, maybe not --- .ci/images/build/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/images/build/Dockerfile b/.ci/images/build/Dockerfile index e076e910..d5f88496 100644 --- a/.ci/images/build/Dockerfile +++ b/.ci/images/build/Dockerfile @@ -9,7 +9,11 @@ RUN apt-get update && \ apt-get update && \ apt-get install -y --no-install-recommends gcc-8 g++-8 binutils-2.26 && \ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-8 && \ + add-apt-repository ppa:deadsnakes/ppa -y && \ + apt-get update && \ + apt-get install -y --no-install-recommends libpython3-dev && \ rm -rf /var/lib/apt/lists/* + ENV PATH="/usr/lib/binutils-2.26/bin:${PATH}" # Install Protocol Buffers compiler by hand, since Ubuntu 14.04 does not have a recent enough version. From 87ecaca2cf014aaab974b731cc889a3426fe8ec3 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 22 May 2024 15:20:13 +0000 Subject: [PATCH 35/47] Normalize check name handling to match Agent behavior --- .../src/sources/checks/mod.rs | 53 +++++++++++++++---- .../src/sources/checks/scheduler.rs | 6 +-- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 7b6e13b6..8c79a6e9 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -62,8 +62,11 @@ fn default_check_config_dir() -> String { #[derive(Debug, Clone)] struct CheckRequest { - name: Option, - module_name: String, + // Name is _not_ just for display, the 'name' field identifies which check should be instantiated + // and run to satisfy this check request. + // In python checks, this is the module name for the check. + // In core checks, TBD. + name: String, instances: Vec, init_config: CheckInitConfiguration, source: CheckSource, @@ -71,10 +74,13 @@ struct CheckRequest { impl Display for CheckRequest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Source: {} # Instances: {}", self.source, self.instances.len(),)?; - if let Some(name) = &self.name { - write!(f, " Name: {}", name)? - } + write!( + f, + "Name: {} Source: {} # Instances: {}", + self.name, + self.source, + self.instances.len(), + )?; Ok(()) } @@ -87,8 +93,23 @@ impl CheckRequest { check_source_code: None, }) } + + // Ideally this matches + // https://github.com/DataDog/datadog-agent/blob/b039ea43d3168f521e8ea3e8356a0e84eec170d1/comp/core/autodiscovery/integration/config.go#L362 + // but for now, lets just do this + // I don't really need this, just adding where I think it should go + fn _digest(&self) -> String { + let mut digest = self.name.clone(); + let init_config_str = self.init_config.0.len().to_string(); + digest.push_str(&init_config_str); + let instance_str = self.instances.len().to_string(); + digest.push_str(&instance_str); + digest + } } +/// This exists mostly for unit tests at this point +/// its useful to be able to specify literal code to run struct RunnableCheckRequest { check_request: CheckRequest, check_source_code: Option, @@ -223,14 +244,28 @@ impl CheckSource { checks_config.push(CheckInstanceConfiguration(map)); } - let module_name = path + // TODO this does not support the './http_check.d/foo.yaml' pattern + // DirCheckListener needs to be updated to support that format as well. + let file_stem_name = path .file_stem() .expect("File name must have a stem") .to_string_lossy() .to_string(); + let check_name = match &read_yaml.name { + Some(yaml_name) => { + if *yaml_name != file_stem_name { + warn!( + "Name in yaml file does not match file name: {} != {}", + yaml_name, file_stem_name + ); + warn!("Using 'name' field value as the loadable_name."); + } + yaml_name.clone() + } + None => file_stem_name.clone(), + }; Ok(CheckRequest { - name: read_yaml.name, - module_name, + name: check_name, instances: checks_config, init_config, source: self.clone(), diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/scheduler.rs index c08098b4..8c3f1aa7 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/scheduler.rs @@ -106,7 +106,7 @@ impl CheckScheduler { let check_handle = match self.register_check_impl(check) { Ok(h) => h, Err(e) => { - error!(%e, "Could not register check {}", check.check_request.module_name); + error!(%e, "Could not register check {}", check.check_request.name); return Err(e); } }; @@ -124,7 +124,7 @@ impl CheckScheduler { } fn register_check_impl(&mut self, check: &RunnableCheckRequest) -> Result { - let check_module_name = &check.check_request.module_name; + let check_module_name = &check.check_request.name; // if there is a specific source, then this will populate into locals and can be found if let Some(py_source) = &check.check_source_code { return self.register_check_with_source(py_source.clone()); @@ -241,7 +241,7 @@ impl CheckScheduler { info!( "Running check {mname} with {num_instances} instances", - mname = check.check_request.module_name, + mname = check.check_request.name, num_instances = check.check_request.instances.len() ); for (idx, instance) in check.check_request.instances.iter().enumerate() { From a1ac8d62703ad37b81ce792ef492f45a17cf408c Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 22 May 2024 16:47:59 +0000 Subject: [PATCH 36/47] Adds a layer of indirection to support non-python checks --- .../src/sources/checks/listener.rs | 2 + .../src/sources/checks/mod.rs | 149 ++++++++++++++---- .../sources/checks/python_exposed_modules.rs | 2 +- .../{scheduler.rs => python_scheduler.rs} | 37 +++-- 4 files changed, 139 insertions(+), 51 deletions(-) rename lib/saluki-components/src/sources/checks/{scheduler.rs => python_scheduler.rs} (93%) diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index 7f0f1938..24efabd0 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -135,4 +135,6 @@ async fn is_check_entity(entry: &DirEntry) -> bool { pub struct DirCheckListenerContext { pub shutdown_handle: DynamicShutdownHandle, pub listener: DirCheckRequestListener, + pub submit_runnable_check_req: mpsc::Sender, + pub submit_stop_check_req: mpsc::Sender, } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 8c79a6e9..1cdfc1ab 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -27,7 +27,7 @@ use tracing::{debug, error, info, warn}; mod listener; mod python_exposed_modules; -mod scheduler; +mod python_scheduler; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -322,6 +322,69 @@ impl MemoryBounds for ChecksConfiguration { } } +struct CheckDispatcher { + python_scheduler: python_scheduler::PythonCheckScheduler, + check_metrics_rx: mpsc::Receiver, + check_run_requests: mpsc::Receiver, + check_stop_requests: mpsc::Receiver, +} + +impl CheckDispatcher { + fn new( + check_run_requests: mpsc::Receiver, check_stop_requests: mpsc::Receiver, + ) -> Result { + let (check_metrics_tx, check_metrics_rx) = mpsc::channel(10_000_000); + Ok(Self { + check_metrics_rx, + check_run_requests, + check_stop_requests, + python_scheduler: python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone())?, + }) + } + + /// Listens for check requests and dispatches each one to a check scheduler + /// that can handle the requested 'check'. + /// If multiple schedulers can handle a check, it is dispatched to the first one. + fn run(self) -> mpsc::Receiver { + info!("Check dispatcher started."); + + let CheckDispatcher { + mut check_run_requests, + mut check_stop_requests, + mut python_scheduler, + check_metrics_rx, + } = self; + tokio::spawn(async move { + loop { + select! { + Some(check_request) = check_run_requests.recv() => { + info!("Dispatching check request: {}", check_request); + if python_scheduler.can_run_check(&check_request) { + match python_scheduler.run_check(&check_request) { + Ok(_) => { + debug!("Check request dispatched: {}", check_request); + } + Err(e) => { + error!("Error dispatching check request: {}", e); + } + } + } else { + warn!("Check request not dispatched: {}", check_request); + } + }, + Some(check_request) = check_stop_requests.recv() => { + info!("Stopping check request: {}", check_request); + // TODO check wihch one is running it and then stop it on that one + python_scheduler.stop_check(check_request); + } + } + } + }); + + check_metrics_rx + } +} + pub struct Checks { listeners: Vec, } @@ -333,12 +396,18 @@ impl Checks { let Checks { listeners } = self; + let (check_run_requests_tx, check_run_requests_rx) = mpsc::channel(100); + let (check_stop_requests_tx, check_stop_requests_rx) = mpsc::channel(100); + let dispatcher = CheckDispatcher::new(check_run_requests_rx, check_stop_requests_rx).expect("Could create"); + let mut joinset = tokio::task::JoinSet::new(); // Run the local task set. // For each listener, spawn a dedicated task to run it. for listener in listeners { let listener_context = listener::DirCheckListenerContext { shutdown_handle: listener_shutdown_coordinator.register(), + submit_runnable_check_req: check_run_requests_tx.clone(), + submit_stop_check_req: check_stop_requests_tx.clone(), listener, }; @@ -347,24 +416,43 @@ impl Checks { info!("Check source started."); - select! { - j = joinset.join_next() => { - match j { - Some(Ok(_)) => { - debug!("Check source task set exited normally."); - } - Some(Err(e)) => { - error!("Check source task set exited unexpectedly: {:?}", e); + let mut check_metrics_rx = dispatcher.run(); + + let check_shutdown = listener_shutdown_coordinator.register(); + tokio::spawn(async { + global_shutdown.await; + listener_shutdown_coordinator.shutdown().await; + }); + + tokio::pin!(check_shutdown); + + loop { + select! { + Some(check_metric) = check_metrics_rx.recv() => { + let mut event_buffer = context.event_buffer_pool().acquire().await; + let event: Event = check_metric.try_into().expect("can't convert"); + event_buffer.push(event); + if let Err(e) = context.forwarder().forward(event_buffer).await { + error!(error = %e, "Failed to forward check metrics."); } - None => { - // set is empty, all good here + } + j = joinset.join_next() => { + match j { + Some(Ok(_)) => { + debug!("Check source task set exited normally."); + } + Some(Err(e)) => { + error!("Check source task set exited unexpectedly: {:?}", e); + } + None => { + // set is empty, all good here + } } + }, + _ = &mut check_shutdown => { + info!("Stopping Check source..."); + break; } - }, - _ = global_shutdown => { - info!("Stopping Check source..."); - - listener_shutdown_coordinator.shutdown().await; } } @@ -395,17 +483,14 @@ async fn process_listener( ) -> Result<(), GenericError> { let listener::DirCheckListenerContext { shutdown_handle, + submit_runnable_check_req, + submit_stop_check_req, mut listener, } = listener_context; tokio::pin!(shutdown_handle); let stream_shutdown_coordinator = DynamicShutdownCoordinator::default(); - // Note: This architecture has a single CheckScheduler per listener - // which is likely not the design we want long term - let (check_metrics_tx, mut check_metrics_rx) = mpsc::channel(10_000_000); - let mut scheduler = scheduler::CheckScheduler::new(check_metrics_tx)?; - info!("Check listener started."); let (mut new_entities, mut deleted_entities) = listener.subscribe(); loop { @@ -422,14 +507,6 @@ async fn process_listener( } } } - Some(check_metric) = check_metrics_rx.recv() => { - let mut event_buffer = source_context.event_buffer_pool().acquire().await; - let event: Event = check_metric.try_into().expect("can't convert"); - event_buffer.push(event); - if let Err(e) = source_context.forwarder().forward(event_buffer).await { - error!(error = %e, "Failed to forward check metrics."); - } - } Some(new_entity) = new_entities.recv() => { let check_request = match new_entity.to_runnable_request() { Ok(check_request) => check_request, @@ -439,11 +516,9 @@ async fn process_listener( } }; - info!("Running a check request: {check_request}"); - - match scheduler.run_check(check_request) { + match submit_runnable_check_req.send(check_request).await { Ok(_) => { - debug!("Check request succeeded, instances have been queued"); + debug!("Check request submitted to dispatcher"); } Err(e) => { error!("Error running check: {}", e); @@ -451,7 +526,7 @@ async fn process_listener( } } Some(deleted_entity) = deleted_entities.recv() => { - scheduler.stop_check(deleted_entity); + submit_stop_check_req.send(deleted_entity).await.expect("Could send"); } } } @@ -463,6 +538,12 @@ async fn process_listener( Ok(()) } +trait CheckScheduler { + fn can_run_check(&self, check_request: &RunnableCheckRequest) -> bool; + fn run_check(&mut self, check_request: &RunnableCheckRequest) -> Result<(), GenericError>; + fn stop_check(&mut self, check_name: CheckRequest); +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum PyMetricType { Gauge = 0, diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index 37c410f2..c8172a24 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -25,7 +25,7 @@ pub(crate) fn submit_metric( }; match module.getattr("SUBMISSION_QUEUE") { - Ok(py_item) => match py_item.extract::>() { + Ok(py_item) => match py_item.extract::>() { Ok(q) => { let res = pyo3::Python::with_gil(|py| q.bind_borrowed(py).borrow_mut().sender.clone()); diff --git a/lib/saluki-components/src/sources/checks/scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs similarity index 93% rename from lib/saluki-components/src/sources/checks/scheduler.rs rename to lib/saluki-components/src/sources/checks/python_scheduler.rs index 8c3f1aa7..1c6eb464 100644 --- a/lib/saluki-components/src/sources/checks/scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -17,16 +17,16 @@ impl Clone for CheckHandle { } #[pyclass] -pub struct SenderHolder { +pub struct PythonSenderHolder { pub sender: mpsc::Sender, } -pub struct CheckScheduler { +pub struct PythonCheckScheduler { running: HashMap>)>, agent_check_base_class: Py, } -impl CheckScheduler { +impl PythonCheckScheduler { pub fn new(send_check_metrics: mpsc::Sender) -> Result { pyo3::append_to_inittab!(datadog_agent); pyo3::append_to_inittab!(pyagg); @@ -49,7 +49,7 @@ impl CheckScheduler { Ok(m) => { let sender_holder = Bound::new( py, - SenderHolder { + PythonSenderHolder { sender: send_check_metrics.clone(), }, ) @@ -226,22 +226,27 @@ impl CheckScheduler { Ok(CheckHandle(check_value.as_unbound().clone())) }) } +} +impl CheckScheduler for PythonCheckScheduler { + fn can_run_check(&self, _check: &RunnableCheckRequest) -> bool { + true + } // This function does 3 things // 1. Registers the check source code and gets a handle // 2. Starts a local task for each instance that // queues a run of the check every min_collection_interval_ms // 3. Stores the handles in the running hashmap - pub fn run_check(&mut self, check: RunnableCheckRequest) -> Result<(), GenericError> { - let check_handle = self.register_check(&check)?; + fn run_check(&mut self, check: &RunnableCheckRequest) -> Result<(), GenericError> { + let check_handle = self.register_check(check)?; let running_entry = self .running - .entry(check.check_request.source) + .entry(check.check_request.source.clone()) .or_insert((check_handle.clone(), Vec::new())); info!( - "Running check {mname} with {num_instances} instances", - mname = check.check_request.name, + "Running check {name} with {num_instances} instances", + name = check.check_request.name, num_instances = check.check_request.instances.len() ); for (idx, instance) in check.check_request.instances.iter().enumerate() { @@ -300,7 +305,7 @@ impl CheckScheduler { Ok(()) } - pub fn stop_check(&mut self, check: CheckRequest) { + fn stop_check(&mut self, check: CheckRequest) { info!("Deleting check request {check}"); if let Some((check_handle, running)) = self.running.remove(&check.source) { for handle in running.iter() { @@ -320,14 +325,14 @@ mod tests { #[tokio::test] async fn test_new_check_scheduler() { let (sender, _) = mpsc::channel(10); - let scheduler = CheckScheduler::new(sender).unwrap(); + let scheduler = PythonCheckScheduler::new(sender).unwrap(); assert!(scheduler.running.is_empty()); } #[tokio::test] async fn test_register_check_with_source() { let (sender, _) = mpsc::channel(10); - let mut scheduler = CheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck @@ -345,7 +350,7 @@ class MyCheck(AgentCheck): #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_run_check() { let (sender, mut receiver) = mpsc::channel(10); - let mut scheduler = CheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck @@ -361,7 +366,7 @@ class MyCheck(AgentCheck): check_request, }; - scheduler.run_check(runnable_check_request).unwrap(); + scheduler.run_check(&runnable_check_request).unwrap(); assert_eq!(scheduler.running.len(), 1); assert_eq!(scheduler.running.keys().next(), Some(&source)); @@ -378,7 +383,7 @@ class MyCheck(AgentCheck): #[tokio::test] async fn test_stop_check() { let (sender, mut receiver) = mpsc::channel(10); - let mut scheduler = CheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck @@ -394,7 +399,7 @@ class MyCheck(AgentCheck): check_request: check_request.clone(), }; - scheduler.run_check(runnable_check_request).unwrap(); + scheduler.run_check(&runnable_check_request).unwrap(); assert_eq!(scheduler.running.len(), 1); assert_eq!(scheduler.running.keys().next(), Some(&source)); From 384efcd72dfaad5d3c50d591fbb76c227536aeba Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 22 May 2024 16:57:16 +0000 Subject: [PATCH 37/47] Adds template of core check scheduler --- .../src/sources/checks/corecheck_scheduler.rs | 29 +++++++++++++++++++ .../src/sources/checks/mod.rs | 15 +++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 lib/saluki-components/src/sources/checks/corecheck_scheduler.rs diff --git a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs new file mode 100644 index 00000000..1192f856 --- /dev/null +++ b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs @@ -0,0 +1,29 @@ +use super::*; +pub struct CoreCheckScheduler { + send_check_metrics: mpsc::Sender, +} + +impl CoreCheckScheduler { + pub fn new(send_check_metrics: mpsc::Sender) -> Result { + Ok(CoreCheckScheduler { send_check_metrics }) + } +} + +impl CheckScheduler for CoreCheckScheduler { + fn can_run_check(&self, check_request: &RunnableCheckRequest) -> bool { + // todo + info!( + "Considered whether to run check as corecheck: {:?}", + check_request.check_request + ); + false + } + fn run_check(&mut self, check_request: &RunnableCheckRequest) -> Result<(), GenericError> { + // todo + + Err(generic_error!("unimplemented")) + } + fn stop_check(&mut self, check_name: CheckRequest) { + // todo + } +} diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 1cdfc1ab..a1cf32b2 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -25,6 +25,7 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info, warn}; +mod corecheck_scheduler; mod listener; mod python_exposed_modules; mod python_scheduler; @@ -324,6 +325,7 @@ impl MemoryBounds for ChecksConfiguration { struct CheckDispatcher { python_scheduler: python_scheduler::PythonCheckScheduler, + corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler, check_metrics_rx: mpsc::Receiver, check_run_requests: mpsc::Receiver, check_stop_requests: mpsc::Receiver, @@ -339,6 +341,7 @@ impl CheckDispatcher { check_run_requests, check_stop_requests, python_scheduler: python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone())?, + corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler::new(check_metrics_tx.clone())?, }) } @@ -352,6 +355,7 @@ impl CheckDispatcher { mut check_run_requests, mut check_stop_requests, mut python_scheduler, + mut corecheck_scheduler, check_metrics_rx, } = self; tokio::spawn(async move { @@ -359,7 +363,16 @@ impl CheckDispatcher { select! { Some(check_request) = check_run_requests.recv() => { info!("Dispatching check request: {}", check_request); - if python_scheduler.can_run_check(&check_request) { + if corecheck_scheduler.can_run_check(&check_request) { + match corecheck_scheduler.run_check(&check_request) { + Ok(_) => { + debug!("Check request dispatched: {}", check_request); + } + Err(e) => { + error!("Error dispatching check request: {}", e); + } + } + } else if python_scheduler.can_run_check(&check_request) { match python_scheduler.run_check(&check_request) { Ok(_) => { debug!("Check request dispatched: {}", check_request); From 0c400dc609edaca3dc7eb717744923245aeba19f Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 22 May 2024 18:41:32 +0000 Subject: [PATCH 38/47] Implements basic corecheck scheduler --- dist/conf.d/smoke.yaml | 2 + .../src/sources/checks/corecheck_scheduler.rs | 163 +++++++++++++++++- .../src/sources/checks/mod.rs | 10 ++ .../sources/checks/python_exposed_modules.rs | 36 ++-- .../src/sources/checks/python_scheduler.rs | 6 +- 5 files changed, 196 insertions(+), 21 deletions(-) create mode 100644 dist/conf.d/smoke.yaml diff --git a/dist/conf.d/smoke.yaml b/dist/conf.d/smoke.yaml new file mode 100644 index 00000000..b5cfb2cc --- /dev/null +++ b/dist/conf.d/smoke.yaml @@ -0,0 +1,2 @@ +instances: + - diff --git a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs index 1192f856..416754df 100644 --- a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs @@ -1,29 +1,174 @@ +use tokio::task::JoinHandle; + use super::*; + +struct CheckHandle(JoinHandle>); + +#[derive(Debug, Clone, Copy)] +enum CoreChecks { + Smoke(SmokeCheck), +} + +#[derive(Debug, Clone, Copy)] +struct SmokeCheck {} +impl SmokeCheck { + const NAME: &'static str = "smoke"; + + fn run( + &self, send_metrics: mpsc::Sender, config: CheckInstanceConfiguration, + ) -> JoinHandle> { + debug!("Beginning execution of Smoke Check Instance {:p}", self); + tokio::spawn(async move { + loop { + send_metrics + .send(CheckMetric { + name: "smoke_metric".to_string(), + metric_type: PyMetricType::Gauge, + value: 1.0, + tags: vec!["hello:world".to_string()], + }) + .await + .map_err(|e| generic_error!("Failed to send metric: {}", e))?; + + tokio::time::sleep(Duration::from_millis(config.min_collection_interval_ms().into())).await; + } + }) + } +} + +impl CoreChecks { + fn from_name(name: &str) -> Option { + match name { + SmokeCheck::NAME => Some(CoreChecks::Smoke(SmokeCheck {})), + _ => None, + } + } + + fn name(&self) -> &'static str { + match self { + CoreChecks::Smoke(_) => SmokeCheck::NAME, + } + } + + fn run( + self, send_metrics: mpsc::Sender, config: CheckInstanceConfiguration, + ) -> JoinHandle> { + match self { + CoreChecks::Smoke(check) => check.run(send_metrics, config), + } + } +} + pub struct CoreCheckScheduler { send_check_metrics: mpsc::Sender, + running: HashMap>, } impl CoreCheckScheduler { pub fn new(send_check_metrics: mpsc::Sender) -> Result { - Ok(CoreCheckScheduler { send_check_metrics }) + Ok(CoreCheckScheduler { + send_check_metrics, + running: HashMap::new(), + }) } } impl CheckScheduler for CoreCheckScheduler { - fn can_run_check(&self, check_request: &RunnableCheckRequest) -> bool { - // todo + fn can_run_check(&self, runnable: &RunnableCheckRequest) -> bool { info!( "Considered whether to run check as corecheck: {:?}", - check_request.check_request + runnable.check_request ); - false + CoreChecks::from_name(&runnable.check_request.name).is_some() } - fn run_check(&mut self, check_request: &RunnableCheckRequest) -> Result<(), GenericError> { - // todo + fn run_check(&mut self, runnable: &RunnableCheckRequest) -> Result<(), GenericError> { + let check = match CoreChecks::from_name(&runnable.check_request.name) { + Some(check) => check, + None => return Err(generic_error!("Check is not supported, can't run it.")), + }; + info!( + "Running {} instances of check {}", + runnable.check_request.instances.len(), + check.name() + ); + for instance in &runnable.check_request.instances { + let handle = check.run(self.send_check_metrics.clone(), instance.clone()); + self.running + .entry(runnable.check_request.source.clone()) + .or_default() + .push(CheckHandle(handle)); + } - Err(generic_error!("unimplemented")) + Ok(()) } fn stop_check(&mut self, check_name: CheckRequest) { - // todo + let removed = self.running.remove(&check_name.source); + if let Some(removed) = removed { + for handle in removed { + handle.0.abort(); + } + } else { + warn!("Tried to stop a check that wasn't running") + } + } +} + +#[cfg(test)] +mod tests { + use super::super::*; + use super::*; + use tokio::sync::mpsc; + + #[tokio::test] + async fn test_new_check_scheduler() { + let (sender, _) = mpsc::channel(10); + let scheduler = CoreCheckScheduler::new(sender).unwrap(); + assert!(scheduler.running.is_empty()); + } + + #[tokio::test] + async fn test_can_run_with_no_checks() { + let (sender, _) = mpsc::channel(10); + let scheduler = CoreCheckScheduler::new(sender).unwrap(); + let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let check_request = source.to_check_request().unwrap(); + + let runnable_check_request: RunnableCheckRequest = check_request.into(); + assert!(!scheduler.can_run_check(&runnable_check_request)); + } + + #[tokio::test] + async fn test_can_run_with_valid_check() { + let (sender, _) = mpsc::channel(10); + let scheduler = CoreCheckScheduler::new(sender).unwrap(); + let source = CheckSource::Yaml((PathBuf::from("/tmp/smoke.yaml"), "instances: [{}]".to_string())); + let check_request = source.to_check_request().unwrap(); + + let runnable_check_request: RunnableCheckRequest = check_request.into(); + assert!(scheduler.can_run_check(&runnable_check_request)); + } + + #[tokio::test] + async fn test_execution_with_single_instance() { + let (sender, mut receiver) = mpsc::channel(10); + let mut scheduler = CoreCheckScheduler::new(sender).unwrap(); + let source = CheckSource::Yaml((PathBuf::from("/tmp/smoke.yaml"), "instances: [{}]".to_string())); + let check_request = source.to_check_request().unwrap(); + + let runnable_check_request: RunnableCheckRequest = check_request.into(); + scheduler + .run_check(&runnable_check_request) + .expect("Failed to run check"); + + assert_eq!(scheduler.running.len(), 1); + + let check_metric = CheckMetric { + name: "smoke_metric".to_string(), + metric_type: PyMetricType::Gauge, + value: 1.0, + tags: vec!["hello:world".to_string()], + }; + let check_from_channel = receiver.recv().await.unwrap(); + assert_eq!(check_from_channel, check_metric); } } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index a1cf32b2..a4dd4b3e 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -87,6 +87,15 @@ impl Display for CheckRequest { } } +impl Into for CheckRequest { + fn into(self) -> RunnableCheckRequest { + RunnableCheckRequest { + check_request: self, + check_source_code: None, + } + } +} + impl CheckRequest { fn to_runnable_request(&self) -> Result { Ok(RunnableCheckRequest { @@ -442,6 +451,7 @@ impl Checks { loop { select! { Some(check_metric) = check_metrics_rx.recv() => { + info!("Received check metric: {:?}", check_metric); let mut event_buffer = context.event_buffer_pool().acquire().await; let event: Event = check_metric.try_into().expect("can't convert"); event_buffer.push(event); diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index c8172a24..02362d1b 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -1,3 +1,5 @@ +use tracing::{event, trace}; + use super::*; /// submit_metric is called from the AgentCheck implementation when a check submits a metric. @@ -12,9 +14,12 @@ pub(crate) fn submit_metric( module: &Bound, _class: PyObject, _check_id: String, mtype: i32, name: String, value: f64, tags: Vec, hostname: String, _flush_first_value: bool, ) { - debug!( + trace!( "submit_metric called with name: {}, value: {}, tags: {:?}, hostname: {}", - name, value, tags, hostname + name, + value, + tags, + hostname ); let check_metric = CheckMetric { @@ -30,11 +35,11 @@ pub(crate) fn submit_metric( let res = pyo3::Python::with_gil(|py| q.bind_borrowed(py).borrow_mut().sender.clone()); match res.try_send(check_metric) { - Ok(_) => debug!("Successfully sent metric"), + Ok(_) => { /* nothing to do, success! */ } Err(e) => error!("Failed to send metric: {}", e), } } - Err(e) => error!("Failed to extract SUBMISSION_QUEUE: {}", e), + Err(e) => unreachable!("Failed to extract SUBMISSION_QUEUE: {}", e), }, Err(e) => { // This is a fatal error and should be impossible to hit this @@ -82,38 +87,49 @@ pub fn aggregator(m: &Bound<'_, PyModule>) -> PyResult<()> { #[pyfunction] fn get_hostname() -> &'static str { - debug!("Called get_hostname()"); + trace!("Called get_hostname()"); "stubbed.hostname" } #[pyfunction] fn set_hostname(hostname: String) { - debug!("Called set_hostname({})", hostname); + trace!("Called set_hostname({})", hostname); // In a function context without a struct, we cannot actually "set" the hostname persistently. } #[pyfunction] fn reset_hostname() { - debug!("Called reset_hostname()"); + trace!("Called reset_hostname()"); // Similar to `set_hostname`, we cannot reset without a persistent structure. } #[pyfunction] fn get_config(config_option: String) -> bool { - debug!("Called get_config({})", config_option); + trace!("Called get_config({})", config_option); false } #[pyfunction] fn get_version() -> &'static str { - debug!("Called get_version()"); + trace!("Called get_version()"); "0.0.0" } #[pyfunction] fn log(message: String, level: u32) { - debug!("{level} Log: {}", message); + match level { + // All except trace are from python3 logging levels + // https://docs.python.org/3/library/logging.html#levels + // Trace is manually specified in datadog_checks_base + // https://github.com/DataDog/integrations-core/blob/458274dfd867b40e368c795574b6d97a9b7e471d/datadog_checks_base/datadog_checks/base/log.py#L20-L21 + 40 => tracing::event!(tracing::Level::ERROR, "Python Log: {}", message), + 30 => tracing::event!(tracing::Level::WARN, "Python Log: {}", message), + 20 => tracing::event!(tracing::Level::INFO, "Python Log: {}", message), + 10 => tracing::event!(tracing::Level::DEBUG, "Python Log: {}", message), + 7 => tracing::event!(tracing::Level::TRACE, "Python Log: {}", message), + _ => tracing::event!(tracing::Level::TRACE, "Python Log: {}", message), + }; } #[pyfunction] diff --git a/lib/saluki-components/src/sources/checks/python_scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs index 1c6eb464..195f1817 100644 --- a/lib/saluki-components/src/sources/checks/python_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -254,7 +254,7 @@ impl CheckScheduler for PythonCheckScheduler { let init_config = check.check_request.init_config.clone(); let check_handle = check_handle.clone(); - debug!("Spawning task for check instance {idx}"); + trace!("Spawning task for check instance {idx}"); let handle = tokio::task::spawn(async move { let mut interval = tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms().into())); @@ -277,6 +277,8 @@ impl CheckScheduler for PythonCheckScheduler { .set_item("instances", instance_list) .expect("could not set instance list"); + // TODO should this be moved up outside of the loop? + // I kind of think so, this makes a new check instance every tick let pycheck = match check_handle.0.call_bound(py, (), Some(&kwargs)) { Ok(c) => c, Err(e) => { @@ -295,7 +297,7 @@ impl CheckScheduler for PythonCheckScheduler { .extract(py) .expect("Can't read the string result from the check execution"); - debug!("Check execution returned {:?}", s); + trace!("Check execution returned {:?}", s); }) } }); From 3e0142e665896eb07b504ab4431b7e3bca4f6db3 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Wed, 22 May 2024 20:34:18 +0000 Subject: [PATCH 39/47] Very very rough wasm check impl --- Cargo.lock | 1138 ++++++++++++++++- dist/checks.d/smokea.wasm | Bin 0 -> 1855443 bytes dist/conf.d/smokea.yaml | 2 + lib/saluki-components/Cargo.toml | 2 + .../src/sources/checks/mod.rs | 13 + .../src/sources/checks/wasmcheck_scheduler.rs | 142 ++ lib/saluki-components/wit/check.wit | 8 + 7 files changed, 1295 insertions(+), 10 deletions(-) create mode 100644 dist/checks.d/smokea.wasm create mode 100644 dist/conf.d/smokea.yaml create mode 100644 lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs create mode 100644 lib/saluki-components/wit/check.wit diff --git a/Cargo.lock b/Cargo.lock index c38c9a43..1377db9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -91,6 +97,12 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arc-swap" version = "1.7.1" @@ -299,7 +311,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.2", "rustc-demangle", ] @@ -324,7 +336,7 @@ dependencies = [ "bitflags 2.5.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.10.5", "lazy_static", "lazycell", "log", @@ -406,12 +418,95 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "cap-fs-ext" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc2d2954524be4866aaa720f008fba9995de54784957a1b0e0119992d6d5e52" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "cap-net-ext" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c81d79ea9c71a1438efd417c788214bc9e7986046d3710b6bbe60da4d8275" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00172660727e2d7f808e7cc2bfffd093fdb3ea2ff2ef819289418a3c3ffab5ac" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "270f1d341a2afc62604f8f688bee4e444d052b7a74c1458dd3aa7efb47d4077f" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd9187bb3f7478a4c135ea10473a41a5f029d2ac800c1adf64f35ec7d4c8603" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91666f31e30c85b1d2ee8432c90987f752c45f5821f5638027b41e73e16a395b" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix", + "winx", +] + [[package]] name = "cast" version = "0.3.0" @@ -531,6 +626,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -580,6 +681,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -589,6 +699,116 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-bforest" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29daf137addc15da6bab6eae2c4a11e274b1d270bf2759508e62f6145e863ef6" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de619867d5de4c644b7fd9904d6e3295269c93d8a71013df796ab338681222d4" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.5", + "log", + "regalloc2", + "rustc-hash", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f5cf277490037d8dae9513d35e0ee8134670ae4a964a5ed5b198d4249d7c10" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3e22ecad1123343a3c09ac6ecc532bb5c184b6fcb7888df0ea953727f79924" + +[[package]] +name = "cranelift-control" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ca3ec6d30bce84ccf59c81fead4d16381a3ef0ef75e8403bc1e7385980da09" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eabb8d36b0ca8906bec93c78ea516741cac2d7e6b266fa7b0ffddcc09004990" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b42630229e49a8cfcae90bdc43c8c4c08f7a7aa4618b67f79265cd2f996dd2" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d1e36361805dfe0b6cdfd5a5ffdb5d03fa796170c5717d2727cbe623b93a0" + +[[package]] +name = "cranelift-native" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aea85a0d7e1800b14ce9d3f53adf8ad4d1ee8a9e23b0269bdc50285e93b9b3" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.108.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac491fd3473944781f0cf9528c90cc899d18ad438da21961a839a3a44d57dfb" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools 0.12.1", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + [[package]] name = "crc32fast" version = "1.4.0" @@ -610,7 +830,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -631,7 +851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -703,6 +923,15 @@ dependencies = [ "rand_distr", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "deranged" version = "0.3.11" @@ -738,6 +967,47 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dunce" version = "1.0.4" @@ -750,12 +1020,27 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -814,12 +1099,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "figment" version = "0.10.19" @@ -872,6 +1174,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -980,6 +1293,28 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.5.0", + "debugid", + "fxhash", + "serde", + "serde_json", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1006,6 +1341,11 @@ name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator", + "indexmap 2.2.6", + "stable_deref_trait", +] [[package]] name = "glob" @@ -1067,6 +1407,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1328,6 +1677,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "idna" version = "0.5.0" @@ -1356,6 +1711,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1370,6 +1726,28 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "io-extras" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b" +dependencies = [ + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.12" @@ -1390,12 +1768,41 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "ittapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" +dependencies = [ + "cc", +] + [[package]] name = "jobserver" version = "0.1.31" @@ -1519,6 +1926,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" version = "0.2.155" @@ -1542,10 +1955,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -1563,6 +1986,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1594,12 +2026,27 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -1713,7 +2160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400" dependencies = [ "indexmap 1.9.3", - "itertools", + "itertools 0.10.5", "ndarray", "noisy_float", "num-integer", @@ -1812,6 +2259,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d" +dependencies = [ + "crc32fast", + "hashbrown 0.14.5", + "indexmap 2.2.6", + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -2026,6 +2485,12 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "plotters" version = "0.3.5" @@ -2060,6 +2525,17 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2138,7 +2614,7 @@ checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck 0.5.0", - "itertools", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -2158,7 +2634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.61", @@ -2225,6 +2701,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "pyo3" version = "0.21.2" @@ -2406,6 +2891,30 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + [[package]] name = "regex" version = "1.10.4" @@ -2485,8 +2994,10 @@ checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", + "itoa", "libc", "linux-raw-sys", + "once_cell", "windows-sys 0.52.0", ] @@ -2618,6 +3129,8 @@ dependencies = [ "tracing", "tracing-test", "url", + "wasmtime", + "wasmtime-wasi", ] [[package]] @@ -2821,6 +3334,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.201" @@ -2862,6 +3381,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -2895,6 +3423,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2939,11 +3476,20 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "snafu" @@ -2982,6 +3528,18 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "subtle" version = "2.5.0" @@ -3016,6 +3574,22 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-interface" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" +dependencies = [ + "bitflags 2.5.0", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", + "winx", +] + [[package]] name = "target-lexicon" version = "0.12.14" @@ -3203,6 +3777,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.11.0" @@ -3449,6 +4057,18 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "unindent" version = "0.2.3" @@ -3484,6 +4104,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + [[package]] name = "valuable" version = "0.1.0" @@ -3575,6 +4201,362 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-encoder" +version = "0.207.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d996306fb3aeaee0d9157adbe2f670df0236caf19f6728b221e92d0f27b3fe17" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.208.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6425e84e42f7f558478e40ecc2287912cb319f2ca68e5c0bb93c61d4fc63fa17" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmparser" +version = "0.207.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19bb9f8ab07616da582ef8adb24c54f1424c7ec876720b7da9db8ec0626c92c" +dependencies = [ + "ahash", + "bitflags 2.5.0", + "hashbrown 0.14.5", + "indexmap 2.2.6", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.207.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2d8a7b4dabb460208e6b4334d9db5766e84505038b2529e69c3d07ac619115" +dependencies = [ + "anyhow", + "wasmparser", +] + +[[package]] +name = "wasmtime" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92a1370c66a0022e6d92dcc277e2c84f5dece19569670b8ce7db8162560d8b6" +dependencies = [ + "addr2line", + "anyhow", + "async-trait", + "bumpalo", + "cc", + "cfg-if", + "encoding_rs", + "fxprof-processed-profile", + "gimli", + "hashbrown 0.14.5", + "indexmap 2.2.6", + "ittapi", + "libc", + "libm", + "log", + "mach2", + "memfd", + "memoffset", + "object 0.33.0", + "once_cell", + "paste", + "postcard", + "psm", + "rayon", + "rustix", + "semver", + "serde", + "serde_derive", + "serde_json", + "smallvec", + "sptr", + "target-lexicon", + "wasm-encoder 0.207.0", + "wasmparser", + "wasmtime-asm-macros", + "wasmtime-cache", + "wasmtime-component-macro", + "wasmtime-component-util", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-slab", + "wasmtime-versioned-export-macros", + "wasmtime-winch", + "wat", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dee8679c974a7f258c03d60d3c747c426ed219945b6d08cbc77fd2eab15b2d1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00103ffaf7ee980f4e750fe272b6ada79d9901659892e457c7ca316b16df9ec" +dependencies = [ + "anyhow", + "base64 0.21.7", + "directories-next", + "log", + "postcard", + "rustix", + "serde", + "serde_derive", + "sha2", + "toml", + "windows-sys 0.52.0", + "zstd", +] + +[[package]] +name = "wasmtime-component-macro" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cae30035f1cf97dcc6657c979cf39f99ce6be93583675eddf4aeaa5548509c" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.61", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-component-util" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7ae611f08cea620c67330925be28a96115bf01f8f393a6cbdf4856a86087134" + +[[package]] +name = "wasmtime-cranelift" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2909406a6007e28be964067167890bca4574bd48a9ff18f1fa9f4856d89ea40" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object 0.33.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-environ" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e227f9ed2f5421473723d6c0352b5986e6e6044fde5410a274a394d726108f" +dependencies = [ + "anyhow", + "cpp_demangle", + "cranelift-entity", + "gimli", + "indexmap 2.2.6", + "log", + "object 0.33.0", + "postcard", + "rustc-demangle", + "serde", + "serde_derive", + "target-lexicon", + "wasm-encoder 0.207.0", + "wasmparser", + "wasmprinter", + "wasmtime-component-util", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42edb392586d07038c1638e854382db916b6ca7845a2e6a7f8dc49e08907acdd" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b26ef7914af0c0e3ca811bdc32f5f66fbba0fd21e1f8563350e8a7951e3598" +dependencies = [ + "object 0.33.0", + "once_cell", + "rustix", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe088f9b56bb353adaf837bf7e10f1c2e1676719dd5be4cac8e37f2ba1ee5bc" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-slab" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff75cafffe47b04b036385ce3710f209153525b0ed19d57b0cf44a22d446460" + +[[package]] +name = "wasmtime-types" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f2fa462bfea3220711c84e2b549f147e4df89eeb49b8a2a3d89148f6cc4a8b1" +dependencies = [ + "cranelift-entity", + "serde", + "serde_derive", + "smallvec", + "wasmparser", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4cedc5bfef3db2a85522ee38564b47ef3b7fc7c92e94cacbce99808e63cdd47" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "wasmtime-wasi" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbbe94245904d4c96c7c5f7b55bad896cc27908644efd9442063c0748b631fc" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.5.0", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "once_cell", + "rustix", + "system-interface", + "thiserror", + "tokio", + "tracing", + "url", + "wasmtime", + "wiggle", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-winch" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b27054fed6be4f3800aba5766f7ef435d4220ce290788f021a08d4fa573108" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object 0.33.0", + "target-lexicon", + "wasmparser", + "wasmtime-cranelift", + "wasmtime-environ", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936a52ce69c28de2aa3b5fb4f2dbbb2966df304f04cccb7aca4ba56d915fda0" +dependencies = [ + "anyhow", + "heck 0.4.1", + "indexmap 2.2.6", + "wit-parser", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "208.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00b3f023b4e2ccd2e054e240294263db52ae962892e6523e550783c83a67f1" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.208.1", +] + +[[package]] +name = "wat" +version = "1.208.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ed38e59176550214c025ea2bd0eeefd8e86b92d0af6698d5ba95020ec2e07b" +dependencies = [ + "wast 208.0.1", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -3597,6 +4579,48 @@ dependencies = [ "rustix", ] +[[package]] +name = "wiggle" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89ea6f74ece6d1cfbd089783006b8eb69a0219ca83cad22068f0d9fa9df3f91" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.5.0", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36beda94813296ecaf0d91b7ada9da073fd41865ba339bdd3b7764e2e785b8e9" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro2", + "quote", + "shellexpand", + "syn 2.0.61", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "21.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47d2b4442ce93106dba5d1a9c59d5f85b5732878bb3d0598d3c93c0d01b16b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", + "wiggle-generate", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3628,6 +4652,23 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc69899ccb2da7daa4df31426dcfd284b104d1a85e1dae35806df0c46187f87" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser", + "wasmtime-cranelift", + "wasmtime-environ", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3776,6 +4817,55 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.5.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-parser" +version = "0.207.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c83dab33a9618d86cfe3563cc864deffd08c17efc5db31a3b7cd1edeffe6e1" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.2.6", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + [[package]] name = "yansi" version = "1.0.1" @@ -3807,3 +4897,31 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/dist/checks.d/smokea.wasm b/dist/checks.d/smokea.wasm new file mode 100644 index 0000000000000000000000000000000000000000..4f7d798f12023ff8b89c2224f09b071d3b07cf11 GIT binary patch literal 1855443 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⋙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

z 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

1d=hIMUdkrqa=v(!-&PDk@XlVLwroIFdQz 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�m3jNW z2b4P@|2EE_YtbWlhbM|QI3q0(?Ylqs&)vUGVPT>X(OOcth9tQ2?aw`s^!euRVjCj} z!A4Ul^_sNRZU6bt-M@_wBrzfwSYwGtsd5tRrw+FA8tGMKyJaawluNF%%7a`mNcJc} zZdxQd#{90gU-OZp?#I}F&93O~8^UPn1CT%usUG8phvu9BceVfH15A(8Q5ov)y0D{8 zqj?EZ>IH*shPODW*~HXP@(iOTOdsKXDX2Jm1U&o`e7D6H+9$nI?(eC%|Ir8woq^^U z^D~ILXnE20=qe;)q-a>cf@e@4z$9-`3e!t{fdIeC#sQ}9Q1cnoM!>bWN39yvk0_dz z?_0wXrjO8k2DK5WhybUS5x@-?F-(08!nMt~1#j$cMY|VkFr;Oj`~>_i5!q;narDB* zBdNZt)0fVhOC72^@U~EP%BK5KoWejr?%d>Ik7)Dev*`}P{yK-G8C#I>-S&zx}k zgg~pn%}CpTg{z_W>44cV^Lpnd!Z)T@n?ea7p{kI{?9`qwHg(O2=Tj$NzmwpuL~;y# z8_lX>zUY0SsnQ9)&?(9aoo+uP_o+f>ByiFU}|B9m~*kF`4W%{<4|=ckaOARP@5p8*1I30xQ1hc zRiNO3)RrB;NqKI;G2H}1V_BDUq+ga@s9)52)Fi~5cqZ!MkpB#~%rRuCrmvO9O#>)A zv(Erz`o|;NlmfQIPvyx>OM;~an0~e+Pp|?PQ0_^1?i}VRn~2gEdmm($7Vo?U zIB7=6K0Qa&vhv~CCimW9Qv$X+x6G1voBW1wj+yO2pt;}qyNN?nA0i*-ABuL4=sWb- zxCW6uPUZ2Dca0zBF$7Hq(nwlr=o9M}`4UNFh+dMMiB*k24z4n{MTgKDj{H!l&6WCu zu3$aK%cfV=p;U*MPQIE~QA4Ff-Jo2RH4shg_8KsTTE>E7Cr-2Eb$*g%8EM1K&l0QyY-eLB^!@5r z=q72Q$^S#K6p!4yM>9sQ0m{#V3|Az9f(}W|A@Uqd8Dly!Z)A&Vx5Eukj)&qLMW{M8 z2aaZl`t9H-VBl>uOb$)~4U}}B(`a5r?RH>yBqNhZ%u*$eQUjP=iGWDD<`Y00Hqk*pqP8?{=y4+Hj#} z4A8i-V=F+CPc0nRQKyLCRm09MI>7h}@a2=-T-5rJa8kX3JlRG?IWW_WaV!SFOUNA` znN+(#+(4Ys7(k;&(Fr$HlYurDuJa;*r`)|L|zLS zT<3gPd<3%Mk#U=>YiP#_YG!xoGf&JQj;NG>y6Joq=(F&)%B|^BDwzm)Tw(& zoGcki4>4AnL2-H*PvDuT5TwV#kLKWAW+7S0V2((&j|J93Kp7q1#PpS7T92h;C1!0^ zDZPxKQJjj+2>&yek|;QW=G&jwFso->lXwyx(os~D07$1PAtz2HBsviFrj?)uAQnR# z%xjKx%KNB3KrhTcP4y3dU?8j?ID+T5Y?LpJOu{^s`f11g~*zN(xu1uzJt_sKI%6r6fh zQi>^^5syOBA*@N5$0$BA>d-|Dr{tAM^>QnQnH#Yu%{`Hdc7W??IiVDJ3DuHde4OyB znvUl0i4+`tOd4q%B;Bq=brIHBAs5XbTa+1%cFtz!sGf0QNZ{kBE2);N1VlnPFzZ(c zLvvT*W>Ftx)CXA#qJ%&zqW2>5gX}+@tlFJmR;vh!-0W@iOQV$EenXz0gIGi(@)j_(`jV8_s@*I?<7u z938U^$72b+QEsRG8eHAyI~c;w5?B>Z@M$qug_ zUfZT{I2%QMpMImdJi|Gx9wpcH}kWE5!!Yl-T}gf zDDJO#yQGWGbsv%ba-FRRd^J1K*r9>arPAR&TSJjO17dH)30I&miUJC_h9sxJ@ybWy z$=@yA|5hfm&y+pY$dzf+7lqRhV^g$k;twPYrMx&TOxx?&^WAI-;U=HGWvc0GtpJbh zG3m45^emDsMLRpAt;V6tC7rE{k|c_W+LNqQqXueAbXTf}`S)aEAZjC!tR$p>t`$-& z0Xa?YyY1+W&qtYWHIxEF-`}OmH<6gciL;dAt*m4Jz45A$2rn$> zX%j?G2?DkWUu;oRjkP(%4mAu%G#AOpsFFdhFhCW?=vjz!yl^gbGFPJ`#yL4i`Bjh- zMb{i~TY!{h%2nP_pd$c4ZbC?+q;nw!qGW9wy{?@7qJy}jUUKNgnSgL8TwTbikG}>N zvED@CQ*ek3Is>OVG!fj&)Ds&gf*{IZyvWNg&YwIV)$5(nNmrrHVc($_2fxq6kNE$R zm|8IxGGktj;~;&JN7ro35R)tFCJ+P8ml!WGD)^*9CUxr=2GU1r<>)y_Yx7}=H|1Eu zKHFy@NCl*+CwNAZ2L+;2JKN83kUJZB7IJuH>KZ#>dZos8Ogfo3b0Phesnei9%~lN> z#q2Q$IObEI7*Z-oP_GQT8kMSW-7C;&s0YdSe7^me976*R?p7xG?<|u_c7m**l~|~2 z&V^v+YIHy>caNOi=5z3 zzxKK(*d1xDFRkT~t2xX<+#tzgsDfXMx8t`~1J2Z2o2$MnaH!`>HAk}G`b0%VG4KdE zFZl)<DGWgtK~V#ee|@Cb5#!hd z0myOn*fGL5aW5Q#F~w~zPuQ@?`Z+q*Bxk}u`4!>ly42f)08kSSui#K7H`#3Vs^_LH zHBoljxmR(nuoi;d&6EfwVtg}SwEMLdi%w0kD_n*&z=j-R)ga0nVRdxLH0v{+tC(1= zW~faHx8m;~+MsoEK5+xeE`_0<&I2`HGh37vl~eM`!b*@3{zVePlgTbr8Z6wsRlmN> z8M{5U0aPmN2_}fj`vY`d12Dohz(43VsqH9Z->IQm(rCFln8(YvQlH)>7e&-d82 z8Ry~3d)Vo{bK9wAr?J4Gd6g>IRuA=zpq}FGvyjz5GQ8}!o)gF=8J7xA--(nQ;yO|OXRsJ!(uZQVoXSlnxq|9}@ z1DO~B@KRxV6SeI~bRj`4pyw)*DQOWBfNVVISr zKa@>5`dyNhoz%G>4!>8bGlDRrhScEj-Ki5iDV-j^FVVao8l*~CPznC@L1*+V8UL~Z z1fm3nUj8tE4h;`0b+BpIz=pr}`eejBSAIxy0iW*{@cGwQ2-qAuZhY*5K+tl#WBlY5 ztBFwg$lkxY#y7VccMR$4R^yviF11f|WQ><}j6Z*P*nX+ey~LaAj6Ygl{TkZ8 zRfc$>@zFClV2>kNxDIY2Fez@1lFzM=vA)QR{x!Q>VEQv%APa%f9K!xOYYo%4?G(G zVny!U^N$&gv7~KQ7>(KC=3F$N0sE*J*VP_p!6qevy9p(vI<8 z9ub&7X8iqkaRZL8em$SO-1t3X^xsC|h~o<~2eSIKBY?&{aIUrYbS#~*&8ePze^(UrA!tM#fITDNd@nDgIcKa7up z4%+RjjvGI;()waBgYlOSwQlE;@pxPQE&g8SHvYyE_x%MQg3ibt?X14LWBiLp*SX&; z?N^L7nUxWJmPxD`-}PwATV?{Q!}fKZ&$VyvtbUBC{02>q8UOULRiRriHh#`{s$+cU z;nu5JX~+1r<+YV7+RMfhkG6v4&K=TC$M`Lf3B9Zt-wP4|2Yr+!++=*)BTQ}e(d?Mw`qL`2C$5xIpu|Im`ar$|avGx?(`*`~*&>cf&Y^Pdi+vyme*1gF=b&PL&Om_U`#(!Ai zfMx0*cvRZTWZ02ETxS07V-oUny>)}$^Gm>s@m}_vT{~`kq{YzBYhP{r@bVMP01oF$;~y~o&@e}BE)pta-1$KTPq73gbw#y?#aXt~~a?{)0S-+c!V1u^*C z^=qx`S^dA~)0L$ky}WipBc6QOpBXLt#Bt->&da5f<^IRj)h8#{`#Qc_S-R`#Vd;*D zR>=7u35plitAf2*bPWfy6*=Yx$&VPONuDTwNBah_>mLW~{`1mu`^53qE!VX>zphwk z;PFX$@mIV!c6{|i?ap3nMc?43E+JL6pny{`dI@U@l)?=tuKQaK@`7#Y)y6d zXAcTyIbqze`sVfx#--LJ5YW3|vmgQg+E`OKfEk{~1>d)gPYi(eZ+{S}V;N4v_|6-+BA@Cbps267A@cUK za#i?Ot_hU*f4gA~;`raSUZG**bN;ww(lP$$cdVXnU*9qQ7!vbB-QRUDpuhL1RY014pZ51{!=|(0aM_-RcD#Y_wLZ4`ldInV*xX=zdZ46&W5!GPcNtMTk?VHMcnyE92z6Ng zB)76aqQet(^}|X62#*`Td3;qwiLUX>4?Q9LsquaMD@5ckR|JfoZ~Vx^0-e&`S8yMf z4g0CK^EqzOG2;i8A#JP1r{JJF?OTk0we&b#8ZtkU1)Tj->lCp4y~qm7!s-9GaK;~h z^aJ`WEWQw^6UM#V`(J#N)x1qLY2O1UfAi`(E!UY7AIZ0Su-Y<#`(*-c%XqC`UMva> z<37O|R~w&LqNiVdluP*Pj`0(S70kl;lBeDQH+{YFQ&&8(avX8#b8T5u$9V6qx|u)6 zt>y+CsapxP+eLDcU1${eF9hC?_Ml2n@6qCm`1qih1@d8x6#vb+xW0j8oghA4s~YoN3L^K zGDChHaA2Yu1QE-Ftxz6eb zxD5gH&4I~PDsRqN9b zRadu-NMQa8NUPF2Sd=<{DGKXSqsH4Bv&r8ArZ;ff#&?0dP96t{fXthWi^8_GUUNcN ztoN<7UV*G+{KC?zpa#cyLaD}%8UO4UsIq1J;CcROLmh=Nywms?suLk(pM_Do+BmU# zny1$of6+&YfZ|QmXE2X{=eWT0RmPw04co_C;k8Ot^Qgip-^!dOvE7mI-wNa9EHq

g{ds<(4&C6Cl)(WqAS?jgeAhW``f5+^>w>I7bVq*uMXVn=ImIxu>#&nLI0E!8YTtbfD~g8g#qfZ+HXv%gq&4vD1Iz5xZ& zwP+Lt$p3hA$dl(Af9u*+07Uei$TU~VrRf--*?gj7yj4UI4(i9RMWx!2`+`dPPdA6Z z-ahv0?WgtEvHHdrAZT)$GS^Mo7|d zBc_Jz86w`y4N~2kGS}SFsFXqf=)`jSOOLm1Khb_2TCMw@Y`q*j3nD(`%fEt!KX$z3 z3gdOH@ozU_vc%D(?s@`wSFR^5<$5yAJx`z?kcjWP7L9eMp+C6%Cl9iJQH0*U3muchEi;#JiDS$o-vxClxOlyO?lR-D$iD)S8U3M(Q?S* zjPJfmOf1jC*Wh<|(NIW7-*?p|9=yo-Z60)tpTX1!t^8@xQhemmRcw_1QhxrV`uP$0 z`O~7MKwtRt*!6B><3CTMTv@E;pSMxCacXWLES&tvO zl_MUB>bExZQi$-gx_SL8Yu$*%Kdt%I#T@?!+B{#5^&ct&$kpwZ@%u}UEB%&&K6N3) zs<>bg8KzV!-(0{sMQZ|k-j+U=c=@wZVq0nL9ZhyBfuUJ|qM z&1)+!0Pz2tp={r+@Pyb|I>slj!75o*8Q&zcE~ob=sOi+m-@1ypF@5CgW#kja`vJY1 zIzwZ7z0Jv-VDzm^E3Bzw{MRMz{jzdRvGNI3zanrmUF-;zH?zjeK6C9kXLw^nY@Y^6H9 zPoeiN*~*n0ihh5ZoBPt^tuMK@{rpbr6(R$@9EIJ(kGEfeS#9Obcnxqy?HgOKKhb`5 z>)sQ_Pd&UY4DP?gfxzwv;(U?FOEt};aGzJ!lti*}Bcr_eHg@v;+?W?0 zU;P|5z10)O??1A>a+6>ryd8{x_vjNVH@07A^u==DHa>dAC6;u;c<*_T%8e)(pIBBt zPbbhoUgE(GB8)Nvgen28Aqp0d-XrUB^}eIU7XG_Olz#A^a1E7@))y5k{Tbn_mGSHa z#m>I{HO%eLk&?O1r><37`Hhb~!7g5D{I?|$mmfFYgF+L5`FC4uJigud;A2BP75)oK zNY#4wIdB0Udr~&!aV8^X_LUbElerz=1~AAC?N_e87>|zD*CQfIgYsPdE|yVgaXmgP zFOjn;TD-XE;{7kgGelz{5lcFlTvZn@DH{A3M|&#`5Kcb+aO=yK+ue@wbDZ0)#z62h z&%SiI{Tk!PIW2{KrRSd)6!cd&DGdK>?_7Oy(3wjs zFI6gPaU{63@?u%%yKh>3Lhrit^-Zs)uTN<6(Ju|fu6m>K2bInO~ag4_MAGow~ zg|WdwOP5}y$6vp$^_pd+kyXI-je^>~pLM-bDac;XS$({HL+guy!b{>GgoyQP5A%qr z{2;4RTYht~9Tguv0W|T^Guqlm@8*h+p3-Ghee`%aw~wCejQHr?tbOzh_PX%VySe70 zXViT3Zl2LOJobdF>(k(A1(eWOk&G1z`;iVe_>2OubMUGwOP_(c zo|KCZBQuqngrks+4=*x_cI(jc$&m=PS{yy}T-Mi5@_&P28to=$PbT8GD=A)J*3|=! z?&SGsaL%z0yzG(GTUgddf)C(Q7qXPpQ8CqvP@(0q_Dms*?{XoFF7q$s+mVv7CO!eI zc_RcVC8<4-+aKtt6rz+sMb@RlIg&1tDDG2(wrs*umR4Hahu6IpMA#_3D5vDVcRQ~| zSXdbe3&xnRz@OqLvoawpCxZ2?+Ai!Qf1zE6`@Ho8 zgdD*OSJMv@fT2Ia50%&GM0qYPUPg=C#Djil{KRA83oJc9LD#K+!^)6?KS6(28dqVQ zgU!5ztL%FFRrq}I^eX=RVNGK`35~g#O~z}NX>WN<`_oIy@YYYA!DH|-?A{Qf&(cWv z$Ups|WeCnMJ->g0{FY=e;f1e!CczduR zTCbv?l2lb67M=HcqdrTFc(VGmXxZN`dn_AHK2dhl$&tmVr8Qp@?}T(sJQPw?JS-+4 zODpz4h{N92fQ(|>yTdFk+m>rpy49kG@~NmNCmyQROg@%y`l4;Q;A*hI$#}S%Wo4Uk z5UuDgJ{>)#YRzQhs;+b5lO)^REI%o>VR#liKU#6|nBCbRAK1C=t@4W*vp#t)+9}(W zQBNh( zWILI7Nb7j7*UNS*m#x^Y$~$Q!;8}sAqLoZU!!8*GNt7`$1j$6pXXQRs9~M0ULQS|3 zyr-U@Z2CsQ(Yc+$=n59Sl~4x6rVnC~8QftB`Q9z zd|XVc0mx34a6Qploih6S?Jd;n!;3JBTNg!#U1MoSoanHBPKDXPRTe*CB6p(US^0>c z@kRJ;`3-x!xjh)D&frcpX^|H&sELpn`3*zbDfy!{qQ))4&?&);Qs3P5IS%9wcU$_h^>?>b0@W?=d17ME?0foQewJf?o zR??QOA6Zu9@S+`ge4VU5RY%6pnjeRZ8C{IC4NN3k6bPg>gKtF-_Rr+8j(h>#%e%qK zkzJ7A_`>CLR&-hF-@ZstV#YD?It(mKXayJ|y5V8hXEpWo6jyjEEg z*J4-gyU1ZsZ0VlQi>-N?oZW`r69qvpAKL?U^T^RF-|<@^NBPZ84wCI1Js^LOTQcuj zcP4*SE-*uu7xEPXat~b;MEig=V-ET8+*1C!RW|KGdCQ>n8NQM;+>QD}HC>TOg$(BX z)WDUa-N8dntsX`AD4aGsmz-6ofx|03n&f=RU9-8N=9vq&=>DSJGioG}aR*7iFC+1t zMk!3Lr_cs(^EGf{@itb!^W@Tj-bizF$Dr*E40tNUSAFmrnNWeEkTSU-Y7vOM5LNjK zIH}kY0q1mkk37a3E@vm}5r0Fs@i zn(_zI8OXh_?gWb|e+zEh>z&^%?inAY?7li|!92Nrm>oHrZIa%%*eeOl*I$tZNDa@I zQIcq*u1vicsz*XxZDeM}Lz1m;oIgui*?bFf95cE!TqiEsh5`X;!^Hs()vaT$0@qc* zf(PbKMg&Gzplvj_d>itkL#P}KNnMm^9zb65Xm8k8w}##aKx{VLASa0eA4J5lCnP7x z`vNeBiNX-Hg62zS?CV}yJktQ=mLGVmV{>~;t()8OP+v7<-Pt?$6xoy>z!*EbC20Zr zDt`g)`Pe>Z2IJe;yE<{j1KBafO-Pr#<}a?0nqqfpN%GyR&j=NsX`BXo6(TO*796dJ4m9PViO)@;dsbP@Uy&T zPYZbCXK!y%07*HY31o4K3!pd~`JSaJ6uMC$f1IF)dpjD|n18-~r}sff5LKA&xB`p5eb zzX3KvcPnlArP*}(17;%MO&;gd{bICfNd7+n02%)idVtRHPqeN@)-QA##k1@5vtO*X z%b)#+(X&ssO9sT@EOJ#{D?q1jVn)ZEoKPL`|7~4OxEP-$L8NXF~iV0pl{hb$l;MpSyx~C%(A?v)?yu4^dej3$azb`?1sNlfSs8n8NrM;!#4x!;3q{ zA8nm(uYFbf`cJmkUaBX44GARl#K*rvA=ftk+19Xq^C{KzSFgct*0@OepRq>9W~Nvk zw@=SC+m$O(9sYye_lj8%i?*^94#iT42976B%49mmKR&TGZQhmdMe8;)H4f+OKouLYtM?W_09Hw7nfEnE?=kR~O>nSzg?=q%b z!I`It$HNyHe@HlZ%%>%2f?)?Aco_lW=5_SO*HEyU=4c6&8#;#*$ zCUyKxXIdM-?M(Z%(`en;Zb%48z#xKbUKGRy!y@hh6c;prh=K+c1cg{daUr;%A_kYb ze_!u&p69t+0*PX^^Bez=+-Et@IiK@6pM5d&&u_*DU1(;8V>9rg9%Yt~W#{tDObofY zKWC&)eV%!~cRACa)A988f<_jJ``|D%Zw?c}=b0_tN<6|uGEN7C$p-o0=7Sp9ARqqp zqJS(AlJKZ6j$9x7AJ|l^Uz+QBb+j>rUGz7*FCYxs>>-+jssyvxr~al2B;8X-sfh2A z=V-57Pzs&s)xy1@Oo4k1ksn3LGSbuK{?oj_*k1Xae}(s#*(;~({bTk1ul@JI=oR+f zFWl1^M}j~ymU6g{fGKp$-8(UHFA3&J4$f`el@_*lsvqe-k7^HfFaQhSNcRQ7PktVy+$j_|^A3$``jRkGQRgEYi{K0H2EQ^}kMzCj36qd7bTm`M( zD=f#NUrLnE9Pi(`TqX{y)Jz!xV|=ra?Yn$Lvz~psFy=jTt^OrrtwEPRPP*(~WwdL( zQIh^N6GznR1PG0kCEF*B68=rj{GWu8U-Oyw6ZyHRyP|=PGp~${Sz@>#Ql=0hvn*7` z^*pm|bO{chMPu3eAv}<0HgrdD+HpZF%*%w|gbyU9Xf_mdAdaoedMH*c&s;QGIa>sU z4Qohj=6@rxf89Z{Z!}!=H9bK7W-5Y)95x=A9Y~S$i?cP83CB>`4iKm34JbSON)9B-Fq%lYG1i}3`@})Y*Ex)Q`o=M5dukcoqgZ9$j2g76H9r2ecPNB^DQCj1l#7}8Y^hQbKHKp&r6Kv}{s^JU!J#D_ zHGDiu#kBrq;bIWu=_=v~3(XP8V>`}E(2JCX{osf0w0Wn!a|`pSnG3mTj+S(l@N^zf zKJ>aYzjg^(=a!cGssq&!LVElqF9*gtiqnlKNVd7RwmuryU$y=Q}2Jh5( z$H2Q9i%LkckFziPe7r0?ByuXjFbbQ26`<9TT%W9h7=0(QSn z{0q&5c?BQ@Ye>HBl=P6c9&eQ2c{m$w^=PTb64RG|2%9$9dzSAL#_uu8E(NR=?_ z#aD<7FN}qYsH%caHV3ipNKm?4k)%}FZ^6)nan)|c@Q_~`7f3fTUD#an6qCDrD*GA>GS@k*moyd=)W>bDC*L#@HLJ$p4k>;;XDbiwU zUC@&o(5~RiOM5t-?Kz6PaKT(BP5b1?@$-q#BU}=B^qwB*XDsBA5v75GrY&7zK!96g zF(%BHGZlA9ug68rrN}C>j4hym^Zrl<0n*S0pcRF9QZ>p-u6#&P@Bfd0c6vxjQv2tg`Zq-A84*eMUqW{!QSb?8uy$P z4<8dXyQt#IL)G>P$*$N<$4~*fo~zg+ZZJv}WM5vXDi?*T*PK4(?{8DCEQet}L}uq4 zh-|jAV#w*1Zlz{Pot-$cL93;MuV>d|^|u1(ZVVfCa^4M1`4BU+O6Jc?DaMpH1b980 zRUT~!e?!P}<7g=LmKsUA=n;$a%DH@yd2O^-QE0C0#%bI%yi7T7OY+=FRWVg(O3|A7 znMor62Vi-*uE@JCS-8+l9T5+m&}%#Z=+nT@^C@PbzuWYkyGLQN<$S+v1j;+Yn*1T= zv(ccXdAS=~QxDqdq1tgYk%rWoCaof_92mCb%1}M?ekPI&u%`R?#c=|^+#X{#)*iFqcMQ4? zxFdND^j$v&Tx0$G0Qnl3)U(uTDKHD8B^Gsdb1GYe!WP8RkOZk@lx(!HpE|6;q9~Zi zL3r_8-r_mF*H6V>(DwOYqmQiM_@1-0cYxvr(s#6Z-{*PrI%=7oE%nisXTD|LHjUx$ zQ<%9Zd=fKzakLgmqO582^`y(fI(<7wTMRVoFfi!B<nhb;4tmROkEdX{-R)(XV8jw!eMc2(aZyKkIbmVIlOz;=5hpe;by z(iRAv-M2Q#x@LS?odV^jS2rbZhg;l#BaDgsTwI+Fyrz8sci;E5-H9#pw%g)`_P2-$ z#gZq~N&L{#Ogsdhd}jO9WfU-eJxiR zej20QqvugJMsD}a6`D3v7WvlO`}x?Fvn|rszoy4dTQ5Rc8TxUysb^0q5_m`}B7t`I zMBurwn&OI%8p9}|#&Qm=jwsI@?Ko=g&nYwGp%_Kxg;AJ~B`2^O$8pcRlG7BehonM5 ztL%=8l%Vl0uC?6O>?Ruu(r%s^?da|*x+d@(*(FLrKE^cFl}4K}=~$Y|t8#3|By50{ z$~?Z9agGu-MOU9!RmLolWUd#Y$_Ce6oPUi>QY(nk#wzZ`p8MJuq~gBFMMv#%ho&j=32v42Ad@nIyPrJm5#EK<;k)a@J15h+lQD3 zN0lH3JegC^JXVljAtA%}m{rP~La z_yh&O*uv*VvCC{8nM?lnuDFe{Avy+$-!p$9?0C%rVgV>Mlz9i>vz(|!xtd?BT5y+D$DeTHAp*|NYcrF zJBF0WzrVO}B)k=FJG}MyH@^=sJCR~J3dLZHIP7!X*xc**GZhtigZ#J%r)7F|iO2CJ z5nAyK=_kz8J0lSSn={LxfXk!!0p1?jEIngtfmcEz!08zU7N;AMae92d#pxX*El$rV zK;^B99CjgSZTES5t<;9zX7}h;@*9^rZf^FBDQgeY7KR+Ay*(xq(;N*Gsgjf~6#Crw z_GFfaaFS2%t8|jN&Wgi2A!YB}!QMgfF;NHa!CcE44l*x~(!mSVvOs&4tSH~MQ_yw| zlc=xX?qKW^61wB<)|t>2#ElLz^Uj6%wu{;BXj}#9Pex+>?LK*9A1fxAU4j15XvNYU z=gTZQy}JB!;Dp|5OMVZQ3@(Y!C2I)#7E<@FL3H$fxm|#?7m@S1=+!zNFOGKPeJ%pH z`^~wSUag}mZRYEBhHs8~XL!d*o#8nJ%(az2zuYtYkCVis z*Hp%7Uh^=aS%(~AmRAyR$B*(zuTf%Cb1Qg6N;SNz&^(6aM5k^ZrsKhp6RCvl1fTnn zxCWy?yEEB<1n78zljmRo-fx}gADZ_u6Y2zff_+11^fXmRoK)hUJ-scx%MMy^&(GRE zKA&=21dbCh5pVQX?_4lHgHhM|COc%zC3Y{iqQQ%SK%rtoiO-AH`kx_>>!vBc>r zPg49W23{2NG0y7Gxl*jUN@j(se*=nlp}8nh8tIvPDq0*!a01EzmTO5IS5^6h4zoS*M;`T9XA0C8XO^RARjUQy%@q564Q(M z9PYUl!Qpn|;17M79*+R0C*Qo8XGQwPs`|(&)NFB7doA^sx^jlFf0#+JBF@uax{3bl zJW0EHf4H+-%&N0t9aaKs=YSk()(vk(wkU$jpA|WBd-#X!R`Vz)Ki?86N!INooZ!lN zG*lY-#>qT(RaoYhJI0jq_m(j0zkIY882?mATF9YhbB^lFZK;52{5^(6T$rN&+p;uL zyGCoS%P?9J{t*t>HfvGOGb>R9e~3kWTSYu_;E6nftEF4$o7s_X=MwO-GFHZ2oJxa* zj>nkE_ExXcBgcH32hV9>J7QGvXFy7XWjpcoLu6m~rFx!O0xQ+{uTUx38-I=l!DSL_ zb7K$D^GBO;ZtP$4(r^eX10yXimN@oA4C@mvxbU#4Te^ z@DC?(03H;=&S)_L&)1!89Af5U8cI-wGI`qvb87yNB%*d0iTEr$;~yxHP9zkX-o`N{ z5GbB}m|7}P$_6^~5vN;T(iVjtEhD+9x7keVQ$hvI`xOYEpNuIZ6$abNYMLu5q_ZK6 z10gA(6I!kilb=dBy_E`c3Nbp`#Rsf6#r9`&t-{2vI=dvriCE6PD2$-H7vAYj#Kt)= z5j%xPvE_JPNc%e}-Y_+?C436H?;S{D%)`9fxA_ZC;}7y0C*Yw;OpO5#xk8IYw5N7= zkvy-DU=#Ka1depU1}pUGrYJmR?PwKPgb=Q!oa2G!mnD%SaSTDB@jchZ%vsE5eOQfC0;#f`*o_-Y!pM^MmtV7JiX6HENk(!!`Zjryy6_KDj;GibbiNJrd?WRR=z?BeIt- zBrsX~0HFPSh<(ZXFoon@^PrDg3z0XXP2g6sn|BAe)rom`27jho{Mpqp{^YB$D2S7W zKbavvGm=7ne&(MTTTC=}W@PZsR7&*Qy9>#aKRpqKMRUrN;Y$x@HAIFy4fC05Eu#mSM> zUiA1LV!C@px(~LpJZ}A7kzRv|^}^!>xxTAPos0t8hf#@ z9799&$5He1CI^$b16`)+VtPRGM4@@PmdtEr((CzvT6_m#X$m1hq`>{wV13hU+5 zATSN()N!=0_#akXN1e+sf#~#YMuk=c`qTYlh?QrQ@e*MTe=4yT=l3TVr?>gwoVdOG za&2kkxX{s*3p|uey&pU5`d5Ek*Xot?juz@=@%^h4nk=t=URgnV++0rd-&twr;!>5? z$Q^tlUtdA*J=tXLQFOMXH!hOu8Y;@2!PrOlNgLyVNHz#hWZ&6V$Y>>M71877no?ag zPniZu9z@Z}g!=SIFBWzx;E^;uZxp)oiB##GQvuNKAqUuM=RIGUO9m_0$=ClaGAQ3%F@}BANNiy`RW#NJmZF#X*+HXumBh(VG6DA({b^GG{9a ztWWNEG=j3ofy}qeY_BUe*Jd|(|2Fb(3BO~SCu~HizE_WK=563RMcDhUJt4@XtpDF| z<2^%H0T-YfEM`fL%AYd9=c|wsCEYEg`fy$}j`TK!JAKRx)I^qiXfB}}YKaXo{)GhP zWf4xhuD@(z!F86kJYvO3^AeYNx&&B3)i#0-Xh+Z{l%&lB2H98F<&;Lt?T{8J?#y9( z9;<55a_&}0z$5rvj})x#QW#pafpW~mcLjU;3k0T1*i(0kf;FvRO+1rtt{%ghPBgEe zo{D}raWi;qVXlX17po~j9^DqZjA}Y+d-8-Rh`1MsR1}!U^P4%`F6Tpm)vRTa*8XIr zlA+m}&ST>We}=ni_Qwp@do!GxIm1<&;p(o>@D+cC4}RG*+^reLyEMaZD$;uvp&Aw$ zmW>uNur-fFGce`@9zfgv9>QR`YmWIdZRADoV5gd$)?q-r-ba zXe)dec7OxO2`-KilYq(icVd3|7mN(Q=Q91HpO9e9KM->CZ-$QyXDznL?FFesCcq_n)QSzvldb85n>Z9|IQ zM{e#d4jU}$5f;^7i^^MPL&$#xjJMU5nFWX)l$IcHJY3RYZ3(7kjzg65(i4I~pI}gS zVxpbm$20}hgBMlEq&&oYoL>gn)08Fr^)ORMDRQe#_t@T?V(+HJ>lcTWg?}kk{$VWJ zM?}srmyfj<%$r#vojuI$p(e)2GgEQT{t~f&4N<#UGHI9bqZyd+5QI4dUxL0)qH#sf zsQExvtchbn-&3+I!sj#DbbAS%rcOP1h_f(RGG$dUqS3oTlj_K6<_+~lbQ6b=zc{Q| zv&U8yVIvuQOSRtP<1b{FGpZGbqRLOUqfMreu^sKbtVZvGMnBE$$!|#=^axF{mHGk) z5SndC>K@5GniBoC-5_n=Ak*nop+Vlt*GdIaZ)O&jo8|9q#NW%yV8Ux;;@Zm8!8W}? z3bDCd$lE8Hk3i2K%s)(~aZSsrTOoSfQV<8l6M>&Sta{i6;Z-aho?#QAC>~%5TQ_mhKJneAFr7HS; zLx^uZ!{*?QRESdvAzICZXvO%KlVPS;;&U}^(9op6l?kL{0~Qmajgo}ZABR}f^ewX; zpcCZmZqUIxI@e>}VaQeu9sae6j{@rF#Z2E)a~^KCFj*(tksI|Tp)x0KRLn`j0cO3A zyx$Yb2(74X@hN(1Iz?wT9}r(Eihd0hSrQ~2=i>)j>qG*?nK$8~p@Aus_fy7)o&20# zZ8qa-Ejzi;Fy3d9Hee_9`td10{ExTCPR0{yr;pDFKHkiF4(Ge3F>*-m4=pcwBa79G zm%w1?Fx1Z_@+=!(Z=LF^$G~YmBF@yi%cS@GN$dpCeFm17Ll%X9!?~JQiw&XT@BJwY0YqlJK z8u%KB`jKX50Zi+~VJNqvsp4{nnzw72cPn;XZ@hB!z@yBK1muZvd|&6YQG{1ie5pcQ zHejtyVi>C-E!eUEH42hgQLMvqdoJ$Q$H8OEU9L5FkaLgD?ZYNjHE}xf>g}p8Oa3w`K@Yx85z>r$SXsQ2~GsN zx*SB!_Y~CAk=QQy8RGGGJz}t~0B9)p6qc@rQlDg%3vS37c3l5vtkWEE|I~8Lz2# zFn>P_T;pxU8A!v;GqYzZEWS%);$*+t}PV>?Ra7|G<}jMn@<2sSTO0Q0^IyA_n&t@bGUV2j!b10FdVOVs;xB3dt7~%Uy=)K_vc>Y^cE9 z7)HD`2m%G>`7zA&7L0@#4;4J^0(w@n)c=g+nk#A9rTSdIqg7hBsQEi+(CH+BMa?&W z&aa>!VE>TjNH=l;PPf7TtV&?q$btVY#D#(_8nBOf7;~f~k;T$ImQvir^Ac&}2O`Gs z&LV0lTJCcp2TWKYfLNrEeb?3pf&}$1V&LP=(sK~LB#&PeDj@*rQmk(pejPEz3?KK0 zFYWyB-uhjV+$%Qy^*Gadj?I`QK^Eaccmi^c5Jf)T+*!y<$d>hEom_D93VfnH0R46o zIssDD42ke!p#+e==5~sT47#3Bc0_+G2QBnM;oc>kR9DOtH_wZd z`1m1IjJum?4j!H9FYbbWi%E^ag(I9zN2HrU6t*niJX=6$!4SgwHek%+idkBq5(7Q? z&HIgPgiTIuL(Uk&VqUI+gZ|JG_bpK;eQiXdx6~c17j+*ZOxVg=3NZGykenfDF0PR8 z0>XQC!wua9(hs6Y@;rP8t>$!XeJB@>h~-%lUu(Ov6jZ>M_^er`fu~5n$1cMu!i|Zs zhWtrV0(-F~f60cL`?n>u--&=&5E<~Ih=1r<&XxFnBND4kuLw?2Bfw!5`R04(m}-68~-4{A%(!hlrMJA(Ba#&na9> z&zp5H;;Y=l{d+hDMP&EZKsaVs-pGc&&8&+%LAfYJP^lEfvo6!EdRz+A0Xr zEr@2)-;ZzPcQ5lxPg6e5`zclWG;=fFMZMF^Gn~!E$mG_G7~tLOZ0>LAi-a*Sgg1;5|f6oS3m^*ZFR&{IAem99q9t6Puh*QKzf~1Ra$xyZc2M;y?4Um0nB|JRN z23ec*Y^Kv6nf-K)^i*X$95R0r7t%oj(V6HZve>`|`KAsq` zHQ(G?3+MVR-1tYn**17)L3HoZ*jl_VzgIi3jiaq$s)u>@($C>!!t%2q`9__PTnIB z1FI^$PvHS8^zua19GLt>Fl+lkPu#vuD88{e5VuqlDIv|7C$r;7by9d3tplkJc@>$F zka>~Xh8k&$OYt5D@`qq?8R-F#Wb%WU>?PIBJ|*VaRg^|Z9bRQ@Y=Vsw7FC+E^_&)j zvVzYe#UUq2KH7jV=H#PN>qDA}P) zDGT)>0-HT?PVzGHoa8m{-^S$MDW)thWUAc%xOS@?WCoibCUi{P?ul$qkX($_Ds*z1 zuJ8odX+^+0a=>sWel*)9c1rw^e&3M@h%m>Q!b&HBNmcrsC|H&x`N=?l#IoCP+NGuI z7$U$Z5bahHQIouGxuv1QQfSx~CT_*EONm(gy?GS0QnRmG}NsXEVN}ZsT_Q(mOFpB2lEQ@$Ci@~9P*$3O)91gX>wle0x_8^vjVaCcBxVaLB zVD*Tn5qO8d?{?J{ouOSfuSVLJo0EXn%S=RZ5zt=aI3&d7_JQ`AfHr#{KzkEf1tI(a zgwU7e_KO6%FhcyFxsV9)6bbZ}NV*96nZIj0V2&N{+O?5=@7fRdwQFeQZeQD5;z~*u z=^%GvyMzIchPA*qNo%nSTtn?gU49&u%0@y2h%uXrKxq(GFGyd;znqGiqCD4!mD~NW zf)<7T5Qn&{Qz%zN2c`m%M61SeSfuae>8xBq0HgI5c-4=rfndK^KjKp)t$2HqmvoYr zvfOCy&m9n;*ug?6GA6>^Gf6;dE|gHYdm}ZPq_$=+lY4^AeOMfhQ!XuZs8QY zRoB2J@cq%O5_a(@bB3#YIN6$wPJh z2H?6lD0!a@uA3~lZrUrja+1*QV&s3};R*b%DskK<2;nl9KT0(Gc}Zw4LeS8IOje7>)=$w3~TX6)ny*&r!N~S_pc#b$|g&dQ0&TlhFL-a5YbkoQ&K1uNu zRHz>HHd21FdXqeY7?lzaeMu~aU8#i{7iS_jpqOcdD++BTuO?l4eMGT+9eZlc2| zpJDGM{42Yd%GJkEv07F0`kS_1sGu$;po|;2W*SyV-Iz?OqlA-X!{rY+$gCzfg8Qcf z@ZRQ1{LmnS5M~<2p}c546}By2EJc*)Cy{>x%!-dggL~-Xr52;upS$p!D-q(tV1F#a zVD#2ID6}@Nzp)1fF;juXmQAy;R$xDWq`+U-sE|OgzvnlQwYAfG>PIbrM>0&>t-L#9 zE1z^*`G^vkx|t6+P3X?#ketaeeIRkD?2eXnW=H$+kh!6-h+Zrd3>p>FF*B2>#H_8_ zqtXa|5LrLI^g33-L8n@XO^Zp&7B;e91gTI3P8+m=;#Y2iU?d;aNd0#Bz&;#GeQ7xJ zCL%Z7N0~I`j0OMklJ(M1JGafpZ0$LokA!8mp9~?lF>Aehag0;_2!fu4SxW}YUPo7F zRr#VeB^FW>K}fCh1003&By5RbCknjZ7LD~l7Fm@fUxNbXsR+w?5ZTavgOYbn2?&nZ zWTX=NjcfdGyzG7BYVRAfq8)vM)JlC~w&Ed!PfSB9v0=W!CvNjUF^4!eyNn(9b+wFZ z&@ zvk3hr+uLVWvRI+Nv&fsXz*-xwPoqaGBUZwz0OtncC~1wHY$`ENeixeX3&u8YWfj|7 zSK~P30-MmPkV}GR%3P*t3ptzJK&KTVh;wU-m8Q9q0#YZNQ8-XC#*y1qcxcd-KIg<*;VTWR}j(Ch0Vio7xEX>0ET42a6hti z>V~#r8lfdQ0jSC%KE*r-GvrWThfS3?2!tYX)LHEU9JmDG-=f(aYEyJ3K9db$Z2ab4 zOb-c!)+S*Hf+Dg>_WG=9F|Hn}b%MxFyxCfc_(1-qMKPG{q0&QAiP1x-9u~JM`8(Fp z)|1LiE<*H0>b9SWvHD4lwfr_qYE{{N1>K<(KRPX^nBbtKBop}5%fe6 z_mLcvo@62k!Ez`2QKcYZr>kjn^9KR;kc*`gB-n%9BES~n)1&E(Fz$n|(Ffla3%;eF z1AHQ3D=-jf=}CY=4XGB*G?#@F6@O8Pc_r5@%6+5a}g zJX7aQ)EJei6MBt|QzhpaEu?Zqt=%)w<6C$}Hc!j!CS^l=-K`85&XSJsI?>2J0d_%k z0YYBs)Zvps_91MuG031|4C$aLpb|t8JV-@7yHixPOFJ8L{GB3_ z(Gj~AA|*8VEzlJ=xHOo3k3BbI>_&gif1sFALv>rE_j)_x&WNLI$U)w0tvD)g7~&|i z^$6M$x>2N_NqLfT-X{^T5BN7(8eMz#O+(h_3xkF|m z8ELXRG@q&~NZ zXe}?82U0*Vk4#s$TPd{%A^U{YVE+*2S=H%yrGntY;Q{$@LO;>=V91$Y=*h1-6aJQ~ zH;vk}#!ktJX7D>X$|W~m!~1gtA*aFy14?CPSp2J#NC|V^XCstRXL;O z-C&Qz+ctTiQON@wI)uj-BF!*&S&f7XC@40~!0fOppsgDqrAN9TqHs~NI{07YTF8=z zLNtq-uWmuF`wI$yH-&&5&QHGCh8@2fuIQ_9KI%Zd!ok;pTA1s(2a?az&AcT&Vj%^^ zzHl6gEY1_i+|k!Q%9hP}%kaI~oP)V9>F$veG{y)^6o0#VD3G# zz9%X$#O>8IWcPn5bv|s+#+gZZr4#7q`Xj{NXmmMY*XI@~(dOd5lqXr9w(T^21u@8W zxc!3MdO}ND!)MEh`60pzxJ>T%sj zx;O`WW=sK>SVYE$OS{_5NnZg5<7iKDCah(KjJj_DCBAkUP&))6dDppR+Jz~7S@pa8 z4gHwWR|y8$rj2&ZBI@y;cKK%a&@#5XQ_X?A)`CI6bb0EFS!`a7S0*K+8(~zHNt!}! zLN0I%^&2ld^oXf5=e}lFjp764uDx-zWw3^VCli3d7AV3X5 zub0kcxVgOC>nX0)n-_o_p+j%N@o4E+EsFV5U<{F@bQ7OoV-53sV);ac&yna`u-6!_5H-E z%EWV#vW5fe>j;)sufXKhm<)!Ay1$oIgdt@Lksa2*zJ|UCp5fN1Yaij&7$nnfTXvIm zE8cqmo3Q2VR;>{gjo2AYr_3q5sk4cNM<{G-!WbN__bX33Z65eSn1>#@Uxjz>C&iGJ zugYelRzp4p>HO^>GprD#pGI85P<`<_{42vCH#b6{tYztLbVlZ49?5DjcPq(2TkhH4 z+5-KNBPE0SP_fj77>BLu9_-nl?Vf!*-)y4)3PkxhvjeMF53Ctcsk`Tfk&c%CvxiG|A0ghw5;=Li7y5XbRL3I#J8-PsCQ6*J%RV z&s;yMfqm&_c2am*fu?{93bMMc6v)T;*V?V_M`P~}I%TmdmM+R?LKhX!3pbR&X{yo{ z$j=f}=f(i)+Hx~<1l0g#&3;48h+P3B=M%B;f5C_UNK^^`*pQfMeLN444>}US-*>J8 z+zVhYr01RC{2_w*1cdC7eB)u9Wbz5E?&~TZE*oBIuTQEjaUHHkCdo_Ba9$61N`@}8 zimGtmz^Al42AbW%sj}9urPOqfL8-=*;;tQCPadO=pZe$a;P^d`(Q+Wyuje@SFxQWS zm&uvLL?9iqKQ91G-w=L2g@xDJDn|M%+YWLms=(Yj7Ua?#ruJZsX5I+3Jh_DizCFz+ z>SWY_kKW?d1UWE=1B5%^)AFZG<+J+zPL(_LG}Ca_FMH}+Wyp0VwNkNA^D53w9rdd< zGadDjI_f(R7UKcMs&^$hwd{JhyNekm>{ zEgs{y^Q0vow2_*?Mh$tJQW3UrZhI<4M6iBG&8?DPEpa$j!k3^cuXCOS@!4Zt`SIkA z#Z1LKd2SYpytut>q{diJ^W^_;cX1Y{QoHyj4mkhZC>&OO zr?p|9oZHGSc6Vts?4l4r#_pbSJH{<;UxxSBZe!qe6h~{8f+Q=pypE*3&^FEUG`8-; z(JdU1#S>Mj-xJun8UO~4(>`Zns+8K)E(4C~B0d(ORq022omli~# z4$x%}=n|3`u)Pl2G14@kRS-!0QXMovzg&d&^LTJoOKm}=*N;1(8Xvk?+n+}c%^h!v zMgvaLkXb>^SsmURG@KX}nacVO)nYV~ZlHYTo9OcZh*wHIr%tXCGi7Z z^CtB@6v8q|ZP4222Vvtwub~yvx%gC{z~7~8rQ3)J_tKT(HeHFOd+AEN_NxYat($Nbv}n z@2Zy63Kz{iC|pIX<@!3}aJ2pRAl%7?^-)gT{+>|94qr}Lgg3K`y?-U8zv;c@!k+FS zKStF3HEi8^h!8iclS7CoPo$#bz@C`}D$p}`xE1^?{1{Xmrc$f=vslGV*dX{m_-}RY zZFTkhB_$etVk64`XnKC#Hl{gTjh;{4NKu$4uxa%$S9vx5Kc!e%HK$-UjFDCuygQO- z8hOUbh6Dilf{;$fKr`jMxIIi_B$Y7K74Z8p@coyVh#>MuMAe_$h(-a@1<=hISm8lO8f zaL(_C2+Bk3G>=p(Sr>USW;PI_B!N;OlnMG-S(QzB`8Dd47%n&m}Z6C@}tE5ZA|8z0#4T&81odWtLz% zpU)y_)vg_W;nU!oFZ0cQQndaofk2k6&(QfJliTlJ}iEoF|yFl&`185@Oz7$N;z zN-dY2))DjIOFQ|J^?v^gixYeDt*&2qRc~f|YmdLG7l^*KtMcr1U0IfboYR+u!8dJL z^o5s9oNun{$*0l+v3pyLdJU1Pi-FiN@&3I9VzX0ZcBrdtgm9=NBV?&HLLSeq=`Sw? zD4~*j_Buoi8=7qqimj9V*xGSE%vj(jED%FczG?EYGR;)6%T%$wsux#S@ViNRZ99B4=s$>HKZBAxf?Bx$-qh&P#Co3JJ z6Gsl}qIwrYSdw~|DfRFHbA}w!O-Oc}i3u6&yp(lf#-YZlYRbzzQ6vE#IU z8FrjfAds+f3TXFB485NU{s~8dXd>3n1Of%Y?lM(h9m5-PKSpm>cUi{jE@yRs)%Neg z&`bSbv^#hd>D5wAn?^}qkiz#+9%84;_IoOLDM0Ckli#U>x8S^x4$(luGQ*iUss^$R z(rZmwRQs0WC(u*MdAERWsK`FcDFX&CmQF20R-ySIS2}eQQTI%sfIioFM^;4!&9xfE zO|YTSJoM)(*zbSqD5G`nEOgd`Z6gAkvAko6{a7Mv+$`*B-qKJ{+}f}WxTVFBaTL+p zm9;6-*t5GP8{*!SA#Pe=i0kwQvQ!mCKZy3!&M;pe5k(5XM83WQldiT8rmgLRDR@&F zJ@rJX;~XXRzsQGPkq)eB8JR$nD@1H|`h}O%Z|K^2O6|lPZY)l8u}e#j%WlP zu+>Ud`8MPHTD(NzHRq|Ui!VN;k?{FAcx1#dFQIp+nkTi#O4=#CX<@a{Z{QKY>ZZ(E zD#5)a4vwqFwg1r|VpsB9Wl0*6FLgt%*!PelFzMjUqj6R) zrXIoNg^bbea-%`(+u7-q2{PI;J6eN3+V+gmR=ClY?t8RgxwqNnK9$pCC%7|X$W?C0 zCHo$d39hyioS94Q5iR%4jL{x&qmjPbiRG@bqs{P_yE|jFb&O`K=nOIok!IXU5eBt) z(t;JIZt(jVqdt}xmBu>z7?nkDu+yCAPxHf!0Uu8c*wC2)H`xI%@(27RW5AZgfV1~G zV6g7goxZ3l9kHKIj5>3lqq6R;cA6j3Xv=Q)#Eb!-O$<1rGXrk31HPZ*9hS-dfM=jL z_;!&wBvgvx-Cn>~byfq@ZgA$`f}H3Tb390*k+FyX77fh#FG zjKL1a>`K0pHGL(Sv&|@1umO)syuh1el>n0*e{PVs^q9U>;C}<@~yOXA3~_-2eU#jY+hk zCj9;QjTQ?p5AoG|Q(_oVLB3uwj94(K42%C+4z{L@4~@yIFU zs(}OM9%7EX;Kml~LJWc%(_;^%Nly(?qV6O+^t}|$8iYjEjQi_2I-%cF==~nSr_`8e zs3x*8q#(lre>D)+B@D7YeMjh8z<09hP-Wr69Z! zzm7+Mi{l_<78eIWr5r_5OxSwr$0*Kq)9`rYq+cL<<(i&NR)$l_2K{`G0>Q3IgG}vR zPFsblDPSmBh?C8dx{ivh<(8^{lre`w^B+#{yuP57`z>4|E&SP$#S{`PiwyK+#QTlH zg=5wxr68IoDa$Kc$J1C)leY^RG|0VR=3C_An1nGM}>Jno1@5_Zy9?!R=8fIP<>7X>6xBIMQ^DrB;7L*6E(H$|xv}zne z42eA4i<3&iJ%ogU)M@$Jw=c(fPZps?&bvj-L~;`W&6Z9=jdbRs08xrn28JPGWg(+RpMn-#eIxbdtY>2LZ%RzKR3$a05dG`zuiiUS(f z0N^>x`Q~GptbK))k<3n9^FC2)UojP~b*UcB{!mcuGSnaPc^`5Zv(vQE>j?`;E6o`! zcm^dVtUcg;f&}(v5138ta;iPx30I^eInf~)_G#`NoNvwth&0VG3klr5kQNeV<5@0L zU|nuJLoF@~I#Tc`(%3ufk_hh%YBv_?bx8VKRiOS>x_o(w?p426ck; ze4xO&iPcREMY-bZiRnnSqJENq$v=O!FtMby%LHefpVA-T?Krb8bHN{fCCb2~o)IB$g~<@?Gld+6wh(`D9Enicl9lq~6%~bmyEl4_BB4a^xjG zWxo-@v=gVC=hLK!%PF2A2`ArNB!`3u$=lSFwgd->CyC%xl2N&fW(oxA$Edrb{4E5T zCi-sKcyR&)Heq8(3L-zibRu5dk3Zg5O4)u4KZcIB6Moz`Go`F+@#2-JgD=uJVkFY$ z+-%}Z8xU+gHRS6Y72hmVg)9uoM#7GQ;-!dORZt3}xuqIR-b(CdYTUK8e~`GSL8XNe zW;>7a*zy2hi56yKZ~`AZ!tz?b?iO!QVbD#2zm+;7`xeC;+;-Xg3GKHo2a+xj#Ve_q`Wm6$PUi!SS5$dnEgjW@R0gN!dJxF*0{I!0oKkFZ z6pZR^#?yQX<^Izw_3!Ls7RZwD0@hw^G0xtSn)#tgyhkV_CxM&~Ytv(y*ls-Qa0AmT zFi-y}&g+U(#}c&E`Wo|W$Yv!q7?{MzLLQsA=|a(^-Uhvak^+a4uIj#R9h4FX?`;Sw zu2LVE9Kon$H+r*UCYZ@6VLyiUo0KU~=d0c1wghXF&Q zJ|-blDQVA2Eo{Tc5Qs0Sa*0&O)zy4;r>#T;6odR!ONx94g_71CLBiaF0F+9PIw1h* zLu>S*S14ZIfnalOKmb3K8OePQD_zF-bgrk30*;^KTq{UvKP689+Rjn0K5&qSM#>yCML4nyW>gpCR-P zs!XD?%Wg?TMheEZ?II8F*9_Oxz&eO26z6>vn6PDqg9u3B;+CxPWFC`7w&JYtIgzbb zF^UF>RKB@eCh>`})^yV3*qlw2n&)+w{ft&bELLSxPQaMhjj2!I+^46yux;W3919o4-n8p1p&SGZ=N{D3ki;4 z_XCch?z(``y^~-wsvDd^=oE{)zJk%{04_76 zB75Z~OpWZtBhb~z-o}6h`hr`54PgCp<9s81&R_>Z$MmLw=VCm#BVe-UbLd<}rS`lv zhY|;>uG!NF|IP5$9BRC1-5@mBCcP5%`~@R}Grc>9CQkza(B?@cygP21*1HoQ)m8V7 zr_H@nScJhjW~M@nediNu#9u$27(6T_nD~+rd*$Yvgyhd8m5=niOTmdv4OVV9$oLjt zrH_UV_c{&+(!eb=9HATYkF27Fx@MY0mi90I@*32V+7~;Jydj&zrDR zmXxscIg_$Gm7bKaraO-7rADDPk-D@$cJ#%J9o^xdr-RKulQbiX=@;D6n6oiL3Gjhx z(qLGXMr7jc(~>+2ai{(53Xp4I=WTG3v} zHP-5u{Y21|o)?Ep(`mTqs3a-}UwHr1p*TVzq zCiFFMJ-qFU1*@t%<$B=xG^yYt63*EpTn}@q1KUp$WeHrJDLghX9?PcYC9ZzX1g?%K zu*`-x*C6WFCsCC&f9R*d!Z%4YQiC=)IRiO?%G6%qfqE?GXrgJAirB6 ze>Vf!mu<_d7|X7eYq^=6T-hBI zPFtTESDHl-WCs{>vq*NJzGa~kQ{ew3D3Gh{%7|G>iLyL0AR4Ka zG9ephyF!+48uPiG=w6E9vwq`*;G|5>(>KkMv7w^@>qE@V6J|=o9mXcI9jmClq-Il% ztdv4bsD`U68-!KUR3dxNC>4ry5AC2#;%sW-jG*7ID&p`1Xz-&kbacwSG1SxLiRso`Jk!tf-r1ZQYxb~aA@q((Y$QZq5%uPf&|=&YmBnVtg7hG^Y!5^lS-C9KLn@&dOYathnZebP$X2wW0 zW<1V3u9~fCL=@C3P{Gg-Y?FijTZn*_8nrfV zqA-_RmptnP0eb6dvSy3ab(Nva{eXe}&(VT_74;lqec!D$ptxHqw&TY_G@+}oJF%}6 zgv26ynud{hR_>^=HPkmS_CZv@8W1x}Z6loI6(lDguvp&-B*-Y_nHfAmnj5ZaYQwQ% zp8_w>US8&5K!1$R=0Q5JT6|1u_twH|`|S1>Q$)ZzTCr-|TS#PQ+cRrj%NLIRySaCf z`%2^a-qHHZXoH~0No6!_L`WyQ^1y0wNXt=-1qWB<*%g!Em1no`rJJNqdAZQ+Ew-vZ zTSfiA8AKbAQ}I5a(Z(*vUs7NelMyFz>xQu)*@t6ng>oH7eqA|A=m=Sy5-xTP7r(kg z9XiG~L?g9=$|nNi1S;PB4>{Lr>;}DB0^+r^vj08Vz06jX0e`l(vnM<7ExRUis`uT? zT+`dDJ*Jb+4YCm%B7J^~mn*m4{Io=3+5=hj_B8%u;@oP4&GZB9^PYW>r+(}n$vm$q z2b3>x_6Qrass4Gk_s(D>R zyn|IyAEMH`N(NS zQyqilZ=;zVQ5E^-!3vgt3<{BTV?W==6%wCMb-`X}4l~2C8McD+^0B!MOll_Ol)Z9k zdci#3TdA5z#iN>?kt+DW4ocSYWWHHpn}PjLD%6$;52H{UZP&fH4qy8AqE)_NM25e& zfcH>ycMerQDS0q;gydO7|F5d@8e8rnC3Gs1(J~RY)Ls;X7byXzVA!PDoOO^3RVJR2 zl|{H-c7(cXeO;+1LL8M1{6kT2JpY{xdfKz5e;R6;KfxS|1qw z=Ew?bDEe~+QTU%L*+NGeJHV}qqTDiEU5Ct)?rOm`x;4`MXqUPhoO6AiN@t&`rH1Ux zL9I#KV?f~m)Cltp2AvA2 z3K`BkC1tI<8CTbocEIoGspfeF`R1-+q0_U~Cy(QIdQd18TjGTnWRQ(Glmj5$5_%pu z%N3kWl$uT$4Lj8euYpg(Uf#$PR3KZ^AuJ%B@`>90~~Iy zX8`-6ccswW=wEr-Ju{G|@$Mtb{Eu8m4IulGTM{4f?dWt<&C5R{(*K8%yy1*&ZXo+e zr)LtaDtU))HkGkR^e65RTpXC@hq%`U=(ULbzh1t{l<<=IGoAo`edG;w6AcROP|s2se_E0Bk~n|ksUyemz*($h>BDwj_lokv@Bp^q6?(O@g0Y^Uh7 z>jS~%{$|-&bsxn)WOcqP=*Fza`;uV927ZDfqhWCpV57C;UNTgrZ2OxV3+t`Uph2;! z_zfZSO%64UXw_7rE%Bv6W;tK#&-jr+oUQ&1?t_;_LnmarFYeM8Kd7aUTk42>7Rhf4 z)hVrLl>lp;;+BGiB!uvH)C6EoFM*U*w_X77_N2dX6^OV-|7vfrEBxb&h zi7?@O^Pc8mH|{iV-{uXqbuP^QoKKVH^H9coE(s9yRg~Mw*=i-$pqhx62!z`yRmHUd z>f{&cJ(x|W`bz|7!P?QblkSQrg3=l+*F2O(0z8#Jgco;5pq`{7Y7n-KP6cik5+8fb zD{Z0|4NJ^k?U8QwYTYWjmS zTvE>(thf)w-mQJNs^i4{8ZUv%E-~M?nJ?DneDi=38|)NUgj{7>jP^W z+II+!cQV28aT1^pF>iT*{!MBa(7plvS?0*&s6b;U{%|nyCU4>oc@y6g>Ey)6`$Z%Y zaKRgHU6~F;9v$NhmR-h>%QHuw$;7SUrloxhQ3|YNgbN8W6()Si7&1EohP+1U2wxv| zRe%zZxY7soH36Vs_5gkL80TG^ND@}3Fyxv{a6HQJoI(iVc%`tPf>jFt@Z@ab1{y39 zV=JH?pOd2#vsz9@4zCsFn;XXPyhYq?OiS0y0`N?j`?T#R?^KdMM80LF!{<(chHO>A z%dO--+BT&-Bcf$I0#opr9dto+h|tj;jS~QMyYCa zj6U7=)IerdE7y6|=w^L^8XVbWp#x;FOHBD)g3jEOXDFV>kppC3JLPz_J8XA`>jg@C zk&brMXxB_`b}K6|8*%a#&gNR4xhYHtNE#tzO3aiEeQm)cmluuqRCBqNuOU!XR(KBw2+eV`hGso`cI^3psA+Yb+nJ(~7*S>%Y2ucAmEA>r50Prnr zBm$lyNaFynQDXwG9U#2}ISSYnTo!Z))wgO9r2V5xf^#gy)XwHe3V3}~Y0(1*H)?J) z>g8Z0Rx&+U$&=nn@=TWzWPhw7?yq5Yu!gv|hTZ-ec&5EI-0IfA*_a}XImK*N>;z|{ zkzSub11D}II2%&}R{D@T8@x6X+c2-)R_k8dNSmE0dTm!Icx`I%+S_2OrKtC4)26Zk zW*yriU*s}2Uk7Ri_KSTSbl&4IP=w3z+{Ew%hcFfgmL4VE@XdDkLUWHd{M614|Ch|+ zKk#Bt?0lbbo#M>P5fx=55Ix}J@!&99PF}BdGT)E0>(l4^i4TtFg844=!11h|Z=Ru% z4yJ^(Gspx$*sZi+mn`yrR5**XPqLWTSML;G(Ywz>Xksb9 z=cU>O_ts|KJ2esszC!;7w=M-j`r}=JZhC4YWbP}^t#>M2^4(_-)ZILD>DlEr0`Tqr zMfT}C&W^c2*2QF4`#_$XiK0`dD`0U-sMW?^ zULRGuY&5sXY^*HSbGP%v_b@D6N53)^2d$U2;aT?mRQTP~*{Jl7qWOK#yGoh8v2XzV zi^QV{^ zn~GeaBYQ^*@oR;C(I?U`cVsxq)}KP{q3-KkJGx*VS0HR2o^M{PZAG}JWNcnD{aKcb zEH;}|PBJQo$sV=OT~S>O+L?zDSO%Cv09t^kpUaAORhOH~B2D~D)fUW71`%K~F>SJ3 z)9kd!^Le=<#tWjwjG0G2HuV-d%3M3P)v6vh5}%}C!Z)cz?0jU5yp8zK1=;nyfOq?@ zaEX>`ZpS_mjhtj=sO;D))unn|4R_19nx%KjBPY=wH3D|OQccb|K@O2}iv|a(_Y758 zwp2IpEO2_51`>miqwb}78hhJpf0hB}862u|7*D;PTJJq|2Tvi4J;u+zNG0ElM+TOg zJL~ENHjLa3TClAujuhcjJE~=F1FznvxMxpEV*$wTM(mt!<;P7FMYnU?R7ih1`Uc)O zOz;8D)70X4kD5V8@`+YM2ICD1OV)-?Vx&mEd5cskLZ+BhzIg`aEcx#O^GX)oFndy1 z9sK?XCDj7yi4bTElxUOz25n`(=n#vbN8?_>esMQ{uXt%2eYm5V{tJThJUYO#o6qa- z`PB`Y(jE#o$~ZUPtCq8Ydd0qZkrmK?o*n#XbfXUh-n7LUyzS!WHbvAtK@})BCD*!n zQ7v~C*A}~pm8Vb4+(uVU=4GZEj{Q8gpGrNlZOSI-Y@6=mjPenE^VqP-l8xU(CIuB% zM9O)%x;)skB8Th@$=x+lM^=E&Ya*@uTcVvI47^8+Zku~67-v^?G5A!YBZk%ZlLVW7}2uJk( z>fDCZ{ZycFxl{C`kplt|;RE>45zBeBz@)t#s z>GH+$O+VfpY|gP>mUfx`?L3>z^e5IZpAMM*Ihy1Hgwmt>ht&~Q2`B5a+qm2$k%xss z$8HFt|EieOGh@7SdAoG-$1B^AhjLr^qeWpWR(yDhSBQrmZNgI4XJp~$cGw@3^G&cUoWs$J@_nfJzslpH8W zt{v_>?q{il>4D&!#yN~bq5R^PNqzRXujXKxuf@_-PPca!%p~SjYd=9j{ssqytYXDJ zPCAxsAt08_qSfMBYn)9rdad~w_p?ku>&+RlxaX1U`$EM`JzpvTb7$r3SW3+}K6*(_ zvvAsS@!$`A0qHA;``fC@B1d5G)$Z-CYIBQTAXGpm<2fe31?jFg*yfJ0*EX`aBN^{d zGgSb5qpDP=U$UfDaHyFsN68yi#b#zsJ^$)J+g<1CWKhEH)NF63c6mEB!`rE1w^RSF zo$Bp&%B}W&i7PCT*N=p+fB`5`-c}-xfyv>{ooIp=7r^Pv~ z{M!IkH7jyLM}bsps&G^iZIJ`f+EfM90oZcJKXDXa?qjx(;VUW}Xy<)j4iyrSUmvWB zM^3cGL?F{GW+P+p+C4dtux8+V3_46s5gB34k)lS(Z0I|E>|nbvK7wN!Fs2ulD!!iG zUR*2NNvX#fSkhaT!UL_X z3jKq%Hn!*p-NL9mcBdBfSXJn!*}}!uy0eNqytxUyIbbAs!B5a*jc%=|s@F?vJY*W730+9qsnuH|g1zNjCW#v&*h_W)NRM$p| zPKXR9d+8*^NwqO}tuhwAly-bfI8@JsUDPMK7sU2F2+ziR*Z$}>ik!ILLh|33#rAh! zv$+RToOO1w24Rl(gvuiYgM>V$HNshWwK9lU=Q$C3OGc;bptd>mew8$$0cY__mdWJ( zu#CwyQXUz50aFqUTtJ5$asB7A9Hrbc2Damf22U;Yo|;FsF+KIXLgb&T1dkE*Q^+9` zk}{J_208yXArl`M4gIt?%Oz0Z{(S9VA5_lBfEIGy3LyuG!?|>a|GjuJ8~Cni!^!5f zk!N#t_Lht+Lzff)v&b+dqLk;%Cx1}F*_4_%FO>0bN%(u>gAdXi`S52VeU0?oF&f2O z(#<0POGXyC4`3xM<&|PzdOI`7wd}s2Z^v~Z`I#5J2c`Wofz?Djkt~x1KnLvX#gr^ z{M*F8v_A}fH?SKV_9q9GYf~2ep-8*?YHy{20D_-@#Axr_8UK5ND55g=+?-qHnOSHc z>5G4#oMcLqAfg~ja!QT^=a+D9kEP2afX%xr~hl}b+dv0uH zv^IrG1G^753s7uXkW4h7#eFK-`9MXvLUm*;Rc(PdN`j4R;;^DzgY4^3ycN8(p*u9O z!xlj5T&W~O;JRt3`u*4uZMK~4E~FH?&mA+xs?5x4ub)Z4Q2$JHb&gC@>qc3r4H}f) zvFvN*insb=lQFw$eA)NL(UO(gNbS@)LlWE8OUZ%_X2Au;`kQ~tMW0`wgQw)njiZ!T zEJ?YC*u(8;ZN^=Ago<(&KB%Y~- z9X(WA-=0}-FRN_maFwj^Y=3!9^t^>28?Cnm1*-=H?h;unA0DN`pDcTcY@3^y0LTc^ z9ILf|5nS}KMa%1BmgIs%4l9x6<{F2OTub01GrManSZS{6!FAzekci<*LTc8T*a+`Y zTBjF6?$SD!q2Ky(e0EsUnK~TSM{*rr2tF8Dm}0M6>+kim6#+)(mXQEe792wFl(I5# zF=LnEW{t9I3Byhn!yFfahsNOsM3?Av=bD>jbzV(zT2Iu46gyRyj8{}$NmQ@mT(I2k zB=cV`<@#`eHrM=LB*?o-VJ(m$@`-M+87?1aD3-(()6H34+X#+Rtky#GM6lePid1S* zoK@qQ?Z)&P6J?%V+}zVSl7|mAZ)V5Y%slf*Wf4$4lh1tyL-7C}<6Nno`eDilTTO6Y zJBc`8x!g4nr{}z+*atdFiq#$ip`|@If-~$_A0N@cwd2j5g3^SCjwNA2DMk z`K#Yh=S0{C$tQ;c_%M)r#LC^kaceh^PymF0@UPEC4OmJ8LJvfZ3^mYPR>zBr;6bcH zA!Eg1g^Vd#xjz-ep+|GEk$aWD9ff#MQR$bBY~_)p4=eC%S-2#WpMtYHNg=URbQvim z;ammm<(LaCvO)U`Tb++#|IZSiIME46-bNzDuzc7RTMfX1-Ac> zy)%KYs=D@na`!!#n~5RJh#0M+Rt1OFp^e)1y}s9m4)(SGn%cfvM54CH5JDIeG@>X@ zL2+UTD2h{1L~#hH;7|jiqGAvfMGa2i6cuOve}8+QbMBCV0Mh6G|9PKJE4k?KiCn%BTN2DoJR6HWY+H4)K2bqOqFkbwOG~pGI|7I2E1Kj>- z0yb1cj`X2egxjO;pP~J&9ubPnu6)ihJS&@q0Cy(#DJJ)1emcUpID|a;?I46nrsL!A zmhh49@G&3-AN(k?$YDaw(I7r2Z*6l+1nLJLzjFA15$BuxBe3{+6Qv7bmha}5q=^($ zyIQhZyo0kmL*sRamn1KGi*t;gAuUm^_*oCuTy95JGkA7H&EvaiqSLyjPxMAN(aTzy zXe);(H??MmXwB|CMA4Fzard*UbxYTDnq287@&53ZH)$)AXiS=fRn$D*wNN5~Su$Mm z(OH-iB^JbNF$$gSDvjxB zEiH7>b{2X-3(^v15$Oh+NlW!s74Uf!*?ZYIe>ZM!w!WX^jiKYwJWEADcYNE!WRV=d z*C-dYf0|1$*zRergWRkl{*^v-LfR|eH)UvC4&=z%H90%&&S&p-y7Mi;ww_#I#8v&g ztWzNFNf^tj{ngvSQdA)TzKkm$uHZvjs<5t2ZFoK z$rq&=lPYM1b~{VG_y*!Q2eg@+jMboES3I!F7`Gac7AJ*%;+h2Cz+Y!Y{9Kg=GAiR zqD2-h>&E{!POv5T;Xi1-_Iki?3-FG*a)R~M+u}K|`z!c~@N-s9mFN8(4KQj2KZ#mV ztgtbzL!&+2L9BZKKS2^{Z4^D4G%4W zzl{)=5d3Mm>EPcOfL|40|H1DB_HXgQZ%79JmS*5D2mH7!wlAFlyVvzeo*)RgJ^kxb zcn8@;%ZQhmZOC+PZ(HV5v=@mB*TcaVS>7JumFLwQY5sM}A`VW?^&R8bc43P*dc%u8 zbTlzNbQ7G7Tcw}r?9`s_9+!CeN#bRYBtP-;eiMG#{N*=oz5E2X-pll{SvMXfXFszf zOS${dlG+@8wbA`J-Q;M!M>H zl>qY`6s2ykmoYKy$EJF#OO1VEt^%#@L2>#6e4F$UP3rt6)xm=T2hRMsAD@{Rsv%3< z{EG4M+6!&P7=mZAOb(~d z_ssVK|O;k4M#)IH^ya4P(yn$vFsvMr1yQ#Ml{I+<$ zG1FI~@+LMkXuyTf3N0rA^>ZU@cZeqG` zW+MD6J7Y&(JYK-z@+xiKrb?wnxf7npes$A5jKnP$O_qdlVZMQf)kDyFOj0Ppfw7)0Rg)8z1t- z&WU8YI_F-1i&@Mfsx9zgg-r<33EQT`9;M4U4zq5Jd3-Dnq!D5rqp0UMjctrdeLM|E z<$c7C!@lmdxHEgio&I7zI-hlKe%IpaW>?j#98uOjcKMZ-m@Xs9+%;0SJKUzIfbKIVO-ae;FUd#libiR_|WXJ7*_O~QKJY!y&{ zi9O`XXhFSuS4SaSlm*Bcc(#qPi2Rr!iyEd1xm*a30!Z_0Ch-$&m|X-B`n<_Bda-r* zLf`_pgFxOMVDBAaPph9HB*|cV4^-%ApxUPqpW3VJ<8)$|@?bltTr5;Rxf7_oI~kRm zL8Ue(sGMa{+1GrNnb??)oOcOSPjrMrWHZ1lI)e1BVQ z)V8{dZPkn_(iq4Z%Rr9GrZIB8_REt71FnPmC(m&huup&E`i!CATcrDTj~y@#ILCM(v#e%r6n6@H*oG*t zBlOC0_O&YhsjFxU2b?VfoO-}z5xQD{h>7_&3o}*hw6uO7a_D!=X??VolTwG0J?r6r4}flJ%Ohif&FF*=>LP-fUM zrnF<&Ce$_QjH%62*y8;Ofp=$nz+1<1Fm$$g6nS+w95x#S3BlGoeAv7M-onG?eqyTq z!{%W^1hkZUd|PMDrl>n>=92HA8CPlhKzt)W-Vz~iH=i|c3zJvw1Sa22#^gdU>7O;v zT1@sek0nvx4zs3oA<*u(_kEOJy5lG%xDNIJ-@Z(F1H`xAmt(xox0CM3%L&EORr+!Y zP&24`XPmoQyWd#p#`!nvnc)rLz|80@ydJa75DXQgYodGV-t!C|NO>G;v$*tLIE;JK z{K?pDlmrgvg_*k8njiRyczX}e+SxXUEi=`u0U@qb?Z({x2i>* za}0-$>gI^BX#!hAM6)Gm+&)aMhDN<<*v<-Ue_&SN7;`|KcO)r#ED5|EZTNP^KPjL+ zvZYx~%EwNV(ulgR%xO~4gOr(WMNS)XlE??EeZDS^`Z!-IrSE5YJ(OFqf2=_U3;ZAU zh1wsE)>?N6FSboG{s8l3w93l*zDB(ywxbGl_AfWdTxq1U%;OP+OY;h0lz*04w2yQS z15I5no1%6!qJlpPZN8dVM-`NM{#6bqyH}uI?S_+NCO^d2FPwxjfEoSgZQz_*+6r*sBGUW}tn4o#wJMr{({+-4AQLD!1puaiQd{T-FiMb$8-*ht< zjHp#Ox;aYYaBKYxm5zQ-B`gXF2`e8S)r0knWOr%Ye5_Ro_Op_A}2CuEZc`<9Ch9qCa5T9V;~W?~Qnk-~o` z>5VH3rWO_VHFXo>?D9WXMf2qQez&~XS5ZsL)!3!|pNe^NF!g`*<}vaE(-m&&hnB?5 z35BSq4lOb#sL6g@)NrEJTl1M>iB16e!}#X6P4d`1)KY?Z))ekAxzQyvKEh9Eepxu- zK9&H;#zmwy0wzX1qOiZ$6+5Uqb`mX*^{t)NnbNQ=uB;C@qkQMuZ%RhnooDViLpI0j zCl4Tgy*hfd)q(e`(*rT&jTz9?{KZI|<0Xl~yT9I?$F%P8>r-A))2>Zd$XJEH=$^STe`Lb^J@Jm$~q z5IZk<53{dUU?!9mtiL$UeRvuk1tC8$ktV-D*xHz>H;2MA%Dg|~;LS^Xqe!qMpNyY# z^uL5>;88lBU&^dy(HgJ^L%lO_pwnZsmDr%8%~0Iv&tsFPy80gl1zfCX$m<}Y{$dns z#VBe?=wpXDx`?NHo9X0JR`|mgD7O2v@u!fiOuxV6P?q9*xjcWr#9x&D1V%yhN8W*E z4GM1h1n|P>@w8q>l;j>Pw;5@dn^{oE$@c$fWj*aKl#6Ta1WFmKB}qU(m=-Y`8m1P} zq?KXq0Ba^P13t{>!(#VgVLLwTXJ+6U?*^!8V}NhF-QMO=3Rc2-CGQZmSO{3|qvCl|C zr#$O6(ZVB>FeIy?|Bw?MvGRDc9^*(qb7K~K?l_L3gZTc)TUkA~bW2${BXK$^U$ZoBjPz{+@X^$zgU}YRxTFsZT zOSD_oNZRG48tDZH;C`-M_-LgKGQ9^^;sZ7GtFpV)R2K6`G5@L&#Bp^*8rbKe^b!(> zjdO)K82&l7woe>IEf9wo04S}UP^FEb2r==67I(Z4Q_{{A%td_L=q9E$N|~4ay$B&Q zoDGn&>+SQiV(4QEF>FLMypHHfX37$t8 zkxu+=IbB1-r9vRv_1O!j^mB}~g(0^fEz2Oi2%fWz@qO*m=!(=4Y~fpzETzy&HIcAjs(rb~%URa8fm+~TCBYSL7) z>pHreXkX!iXVX@m)3GMHulaJ6{6lQ4GP_&#&HhQ$uTRA++M7sXsT6L_bt*atWSz`i zB_#sZtnSVHt}m(9Q*`G_7&Nxfm#uB~m?Hub{=RP?JPY+Sur5(7KG_vymt4mLkHGg7 z@{R15Z+;K)V6V$9j2=M(o{P$=oyKc!rdJSdh`yZWoL_dkUelFPR}Hfj92Cn}5~XRL znX4Qyzrc-$Tcm$OotNny5_X})tRx_vTo#E+4xAUP0@=7S@uEZB-yLtp=X07^v(iGH znrreHXi+Ka`%E4i;WnN^u-o!V*c(M0hqL{E=kgdcEkSIhu$6a|wEXhYdbRx^k5Xy2 z?Hv7Us!MD1_{*fn?o)c3*`urNHX`YS+!Je^Fwn%ILZ98u;~509u7t?Qe}UM$+$I5A zJ^>^?47-sd*~5^#B#YUjLGMeIimoo!x7?9Xf1_@kU_;F{A_9ir(tg><70UfC(+53K zm1=lGEW$4NUS6WSzyAAWPth{u6fxSsVuvcgus`^_^a8V$MbN_embA z0B+~#JVSkK_>Qx4Rh;Y<0yxo>=ucay$p4@!#EB$}>)6#nVj0HDGV=tv`8>OXq0Q7W zEYb#C+_Wuj{DghUZ>OrpL~lC|}U*I-7p>at>>&wtojn^~WIbd@Pys z$*5sIn808&CtG_Q7(rImP$iD$f@Di}T?a(3x7h4o=EWhgKNZS1|1c)k#SCV34dPAQ z?9b`)>p}sO54-MT`s?*cZt>wW2lD#og*wNUXRuaE@w6B!qGJ#KB=d8zhqMgh2U1+o z@*EKGUI%7P;ugh6!S|MGO&*;T`DrG9mhhM53=hkSd_TqiXyKP!Y~nVdwht*e#)JB6 zh5lNuzm`vG@($yH{(f*0a`;2~>w!s)x(i)L1ojc;+DQy+hJ*X3cucp;)Z`KDLLe7b zgGd!%w@%B!h4iTi2{^~VwE`XlZve4Ir&@e(m0R=K0gH zx_zg=dSW zH%|ahQ?N2S!^8O%Y?@Xq4_XL?@J4kff?{{>JLBqa% zD|l`UgkxS)!M6QfXzdX54y_&5z+p}-fkm+#zY3!kL;FUR8{ks~K)n<~btsrP zq0YX7BV!4-G-?N*qrtBa2me&XV~3;+UK^NNIDY<}$)2oHp_>i8pyUOo)%e}kileV3 zVS+UL1sjIJH-vffgXo>5Pgk8z3m4DueRxx8weBXny zR{x?~<`#0>xH3*o-|O6|{Ga1o1@}pI9}Z$&vj>Uj2;Je3Ma^W1PL7L%OkJh?_&$@P zjupO&HF}ivf%v)w@rmt&xZy{T%R7L0i|CpX%Iv2>9AgcrM1ReV{-6Q;>|{t(5+*?>NVjL%>ubKb9#pFg!1d&vRQEQolGf?sOme50 zQ$_Vuz{rMY>gkMfP(2&5yAj1De-!eEz0#g)(|o95LwdtTwR065ZtDv9J~K>rG326K zJ!-RXM=jZL@6SO9uj@wH6o&Xg!5?AsQY>Zbh-$<@ceME)bapd$#z8vK&m&i=uArNH zs?2REj+l23XVblmL^eng(>w(K#CL6IBHKkF11tm%(|2Np1Om~#av{6VHS3*hFYH;Rbate+S8S* z_-s^>{XG?5QU{0)b1h4Fx(G6)darQ^o9lb#Qq*2NASh}-NJA}PKMI`B zdN#HMj@`ivaLcqw3B-s#6?ar}Wsc2r1fW@yVYyZpvq*xzdlG@Kv$T4Y@11}K(oO73 zo9ksKD}1qwux}IJ3sDxDkIA~g%Co-V*0x^OU;!hNJHt<6b39K9aAYHYZ{I``3is4$ zCl};IzL#m^1};Qj4D!wVNwR6|6o|gc?YjjM(hwOFQ1%Q$fj((12z#3U z#2vRs{5N*~TQYZg{$ltsw2Gp>SCKN3=<={k2sCoI6W$W3nJ?@kGRZYxogU}v^a)2L z?$4YZNUwuno#&A;Nj2F%&#;LK%<2vU?C-mf*)^RDxcO)UKP+9wt!1@t{40XfoL8ZW zXf0_pfX+NNHk1+*V9W*#jkEO(E*a|h8U|L!cS6wG9rYa@r?{7$uU?)S>nDeX!G}Z0 z?^!P|%cMAtX4?GazGfi~rm?-#&i_kc%7k|O)Q)w2kNO<}=KE&3l5UN4CQvIX{2zAc z8m>O>K*BE&Hf0`0Vbc9qR)d*z$PDt1Q?&;K@pyh>kN^IU^gyW?xEmD$uZ)Xm&Kljs zDFLHdfj@GDJIFPaSdz8Jrx^_ZW0@1TsS&@yFbwjN_hzB_D#D4hwh|3*W}!K!0)@Ov zkFr1g001f7aNvh^FJ@!=!2kt_RS{F)l{07%{#{RusB-#r&9d}FU{nzo3*#j_0V7=^ zr*b{%r^WJ#Wv^noJ&XJmgD{_bZVd-n<}Dm~+6iidg>*fT=2#KW%NBiz9tz=W~%p!GE?CNS#edL?F$auIKr30v0g70evha124;zu!m zH8B+O^?Fcwv=k*%oQUuQWh%5RcagT)$_!slELI*C8k3!pm3$9f3d)sfs6z6d3qO0# z|ExCnY|5nYvv-XjSRu$S)Lb^=y2QrFHn(-N;*Pd3JU?X*>+sedE=$|P9ITof=JT{Y z47ldSGS~chgL!&@6-vplE)o16sw!l9IK#k$q>CPeI}!#Fq2-(L)po8iQ)r3OGmm;dc%tiBjB40DK#+%)WBo$;>!S z2}tc854rYS1V39`_yMj_I-VXK!&T_3iR__hRZ3@V2Y;q@$@wTjTvyr6Iryfvbea8* z&&a=EfGC?V5kzUJFI1+(k4gOPpCbjLpq^;I+(9u5vI&S!{Czc<7N%NVB}xl}2Tj2P!oj}R!|9R#5CC)*qF#4} zP-UTXEeah<{v5pjXC^ndY`QICvv=XYQ1#48Y{oDeR6}`7EzqB4M1GoyR}{KoB4zwX zO?dWf&0>2vt$HKxyP0Q7Nvb`UTS0B~cJ$TQTQI==nGN_3_bEN$0j5ml#@wy-rvMtEnMgBG&&64}Tbo zoRsek@Tu^vj0kQ#fA$8D!OO?yTS2#hje`62DDRM`&iQ_%!CUJs@#3e zY$`SQj}-58o=*4AD+>98=tmVvzk*C}-IlT>!CJppskP2SXW*~(dzB8sdgK6o#vxc) zwH$(O&6yi+rfp_)zP2I{w!wN7&7jkU$(?AGpUY3OT|R?AcuH>Dc8c1(Q`X zi=>8m9My;8U55v|{1W10Ef+X=G=>dAnXR$ul;HImI_!lY;}wiTmEP?2^StKkS3O+%vwGY-Z>Z;)L zM0s7Fn)$hf{zLZs=Fv9EvAM=@&R#f4HO)RYB{n%Jn+tsk2qOn+jlNWW#XHzv@LfT> zzB>wUv??x1HVNp?!T%GCelUgtgl2>zW)wiMyCL5o$XD$GAL#~xb%d4h! zKQ>#I$qOeD^6dQxHi#|cSX0hAT{wx1@gE}_^M?o`?GH7c!JXzt)GY2|1( z5n_H`o@fiI>|cuSPC&GO3uqdt$ex-J^FmkL;t<7xoX}-TRnHIurdu21(#i1}SmunZ zz=CaW*XHH?qUhha4lWp0??#p-D(L)NoSO&Y^7 z%_K}k%)u-v!K}TkN`bRh%6NEIyU-q`tIq&3G^ z!e*+{V=HAqR2kGE;(s4!_@M=1@7w4-FFXG4w)b3uX=w?*>-P+g~0g|&s) zK0t>oY^;{!MTm_LoAoarnGn}k*#7g;h&yZ(Qq@}j+x-3?OX>fUX8nJf-2ZgH|M&d< zSG3;$=YIbWr}Y0t&_9$B?k2yunAE|pxR_h{d(m53vrbMxSJwecTCR)m9sc#+!P)T{ zgeqHQ54$T4>p@<}b?KfQzX7NbbR5><)?i zS5S;eeSIyrft!IGXgA>p?(HEVtlFiA-HU7IS(V%pf?RL~_M0_g``6&JoBSBB35u2K zCN(q1r4VhvsmZ1g4ZNU1njO_T1_9Km+G{ z7wR~~)bw&nbenS`hh=$C3B|6cj1V*v6yK!%L1r6zg8q0vJV60&c)|x*v&^O3Qo7mj z0Xbrsqk`0}a`IMUX(ElV=HUG$j=2Okg~f7dq1qd9Rn^fu5uvboN!R%@`qW`83Te5y zq!Y$f79B=V7o6_uGi6LcFR&>pKMG-gFRt&C0CGM)@VWYE6F!ZikuOzK=6V(pEssoQ zOSGN`X^C3&81lW?S;+KUut8>wG}^DmqIt3x&o&oSkWB%Un&~}V>E(AvHrX{?hKfTm zUN4Zirs8i0NvGesTMV18RAvOJuL{l@w)nmvGXs1gKlcbThPwTcX*<|Gr! zG9QlSqAI8Vb028QBpo{588E$);9B{gJWFeoiI5Rk^t+|0t!YzI!0s&dP+({Jf z**QFAKc7anqc?31EqP2f0Yr*<6$7e1zX9P4*EcEF5AMiG49qL@r5*n{SJvK{G+Ab)R=_Q)C}# z-)`ltr82d&e=pw*+T30ac84t0R<8azyAckT!{$uRh7;oV4jN*8*jOR2MD$W@-#(OT zGZ$dO+t<96ZEdbN^=R7SGPjk&M0PQ`hQs_tPx&Z z0k@B(3kqgAVQIJ+o4)j(71~}_V#K=(w!(qcjMj7#`N-YL3sgsFpp6fBqm->9 zUhj(jx?Lb9aS7*5@^kj?UD=2m99(IEE?64sP@<=u!~QPki@1;8Q90t>i9uY`7K#4Cv(HW^_V^3&+c!`WMj2j_~rc?Q&+B6|HWs zX4j--Y^6qi>|M9b zt350(a<9@k&)o0lD#|rdp}m|x2XKNqaB{fI+=hdjE;={3dL{uE`2L~>(JnB1t|0{n%6`@4BwARDv&O!RPC_B zhrP|#QV!-6{swGvSG1(k)QnU<45*wOFuVQY(L>`L?ryd9?+Ju?g(YA$;T3;idRh{Fd%ey!fITT&- zzbLZY{QPDpnt{(1_s^418NH9oXF^u4wd*c5M}g`HFRy@U-B>}eJlRnqC98S9N4)5- zsDkU|ox6zQ4LMTeu9rpG45R>7@B*a_VZ-wM|C9mj2{yy(vO5u3jg?oKP~Bbq2ez`L z(>hdJtpj~xa;cFP_tgo05h$Ao-mJ*7XF62{0zH&FujVR5>J{434T0sE#gXpvN(pa{ z%ODB4pb(8MS6AI-ImJ@yJP|m`gb1YH-%KZ-(7A>Du$KP&kWm1$b^LskvE)Jhi+t0M zF2>UM19NVU)x?^MCoulq1cTXL7IR(h$<~-PR^Ih00>_*R(>vbB*3hom=mOiz}-5y_fkS!pSIig>x&^Ko4r8cV~M0m|O7%$iW)A z0qcL?SiH>KJVDZq)gp5w>S~=M?}hcGdLcoS=_lDo>7kH2b}>Da09`a3`R|4a_~9q- z+Vsnc)g-;G1-lYsVoS;Zmx?4@yboBsQ?z(UgrM7eOoE!|Ddt5od5Ca{T9+#V3KHl^ zoz{r0xWWN7Vrh3}HS)f|dLol;GP{4G3 z0Fe*p;cjqDRrGx|QWu?!(Dx;UTTLTs@f;hCN!c|irp~7fn}z#B&qmwV*li%i&XyR< zu)fp=(yE3wWJCKWVdsAQs}j*lacV_ex!3JQ@lrO#)A(Y*kr(6@L?@fi%M`9!Y$FAR zXm37*L2xpXUjrr_eR5R}D)y(T(dO&4$QJToIZ`s4tAPE}-Aq$N_$n+dfzm7SUQAlr zPej~n6%4AP#gnyJZ&1kn3l+Yek|NazU_|$-KQ;!M{Azj>`7_i4S!*UzFp5245UwLm#_|Uf6AWl~~xs+jDT}eQcsKt;}IUdB<9t;Rht6 z$npA{OJVSY;<}H%mh%A0`Yy3UfjWnM0G;_fzB=Ibg~AaVgJVR-$JrPkb!vP@w7Za( zu+}=sy-g;B+b$Q%mO?@_H%6IhLCZ3G%i#%AGD$+URy_@tUapoLRFdV};&Z@=8$32m z`OaV5v!r=siP8n6vi(EbPi@UL z>03E*53xB=P=z(oWAf1uXkDH6`A&f5Vn@|B z0yLc4CPG!Mb?m;$1cmH?ZqQ}-JjNVWMQ+IwQ$r+Di!kZC{C?8WT9i{H!2I=?8(JebS3$NIiv{Y@q4m5m_raCW?|Cw zO*h5jze?c9ma@p9Ko-73)}j#3>+K)}_f?f>&|q^zZ^^LHEVogP^Lnf#*Fvw`m2;q& zTqs43A#Bd5M`EQu4ePKuBiV?TqQ|#wBt~zx&OSjhAlF77XvdbtyTV7a%u{h37hU$0 zDv{4AUhHus6tZ*KjMmASi=6loLV9ZeX+g}-%<_D0kSd{YyKZixR6H+(B!hqw?*5%} zaQh45)L$)+ehf$8(8EoSPxT$#Ldfsql7nMLwmGOc`WN#@=ihcI$=>9xA^$dgb)sK$ zC;aLY=8MnYAms%WeIm@M@O=AK*N!~9m{pl%@G=KhdGP}v7|YDj{JyP=+m&R=ayKPW zX-{*f9$vw9#HVAtC*;oSbPo&;pfE;wo1-+ZUx>udffh%2$(${W#E0Y;=o1|XmCz>QtSX+gOx-px3%KYrf;m}(gvE%j} zpYHa-r~q*CXNE2ty_{-&s!gNWwj0GLE#02J7e&}vKi@&4yESmhAH&+J+jRD@J?)*| z(~+4_gYQYw*@4B~iG9`%u#QRZ>_`lJ1eu3ql zCFWEAgBTkay9=BNx+362=W~3zm$e#)gDX1V5?78u|IWApUSu7ni^(`2WZ>X&q}LxSrkk~>J8K()RgZ$g(c1MIf1txUR!y|PSCccsK zM;(Z)xmXpN|G>ous)l^*O;vTggVCZC6AbR=`t z>?w7^0}~NfF}vDR`%|o2P;fy)%;nPa$ao%NvOtcPQ4LIW5EQqQ>)5(Kp)d-;px6@H zratC6&*q&n_uyOQT=o)%EUBZ+lbY=I3$AM@Glhg{7UAKP-eO7zE7$t{9XVb*px#(H zs@5td$P_A1OLG<^fe)YAHn!frM-2v6>&X*dXx=BUVl|H3$BETnN6`e;zoAusEFhUu z7UUAxQ_X{J#QC^XrK{;yzwhDzA)3&ycWu>e-2FNfsxLF0DtC?m5TU~D_55{*l?4KU*FksR@HFxS}`xk|?|mz`|&8|W~CwND)A(~z`d zOC5@kM8n1SHtNgEw$=D|$WCRh-Y2JH>#DJEb*5V)AoA=JlGjO{c-|fIoEF!P$+h&>6h0D;do-RTvmX%Qh*b$?3JNWNfh?BP8bgd;I1~bL5LO#wlzlIJ@91y%K3f|##s=N)% z13g+6cj^u0xaE*A)`7TKAozse{95boxIldgKA$}kc*g^J|> zYA@R0Kswmd)$id2xXbQo-s~FrEgV)^FkknKQu;~B(O4<6ejI1Kr5?v zmYFi14xSz%6xnVey`(b7B5-*a`=lCp7`I*K*mYZLQMbj^1&FJ23tH37#$1>M1e9pe zuO43ovg78jtj>Q70J(kf>dtPYF$g1Jz=1d;$0V3OO``JCxyGDN)do%dZW5+wJ3UR2 zvZU?Q9vD-nZf@^PYK{>;#UICV{R|}`oa%UA(!C;7ECjV4>y*|grL1A14YhQ+j}p-0 zw|P7)eOMIiM$#w|+)JJS!gCdeZI(=e|Nm@&Ij+e340k@<$8(BRMZovze-fo)Kl2h? zSM)5Mko8=5=f9v`*=NZ^j=y<#NT>me5&u5qfxc#yQ`d z7YeW~!CK!=SczbS}dFq~9Om8zjATXXmfZMMMH&BLX1_E+qpNMN0v*N=`#7kX$&9 z=o_~x$%$ne6xEDkB#1|m8uDg_)Q6#^ATysRDfffxuRm29dndXOMGc;TU(*Asbz$6*uEh zHno*rLNJMkNPxK-FpohuX~Dc3t=}P zzvw6}PiCfm@xNhboKiZtbo{`PgNL6nCX^tqok%f&ieh<*he+ds5y>5$v^QR`8%S(@KAZZO9&PX zdb)u3v23B7?Cr7m&zj?0`Qc;nzxpj+OhY)4Qp_}dtQl@G{_#XyPeA$HsKfbd6N!b{ z-UEl}d7Ox?qI$W2zzicyj}fACTEO!Y7Sca?SwE)4ZW^3Vr^5N*M9fcu>p36Lg%f@1 zNP6HP-`x+~>{WWL)!BbxXP;$fPw@q+FtjlH^YFQdk!Gl+*-vxcZO59Cc9o`(O<9w_ zgwi(J8N@fbt??~Cz!?G=zvLravPOf@_Bf*PN zHfnMokM}3n)&mFWC9yUjeWZf`*~c!zU$qGPw6F+o+D6|vmT4D-J|TpT$%ya?7>cS`knp6@dQGQ(>6{( zLCU)m%l_{)UOuD6r?Yvg?ZLrvBnJz{xZ)qDb&G;N=y@rvBM*AsOW%cFdb*+h z?uP2&hWdLMHxCWfq@gZxLwytu6-?>uY!cZip^JxI9JqfeILpa7HHJ^8X7lN1V=R;x zWCu_NpI(vp^awl2(;OUq82MFwK3AW=;y%B&+2^RX%=~* zwYRJEc7AJb*EW9}0RIZFf_Zx#QK8QqW3?`C2DgFDs1lj>lx>tIdT zLDB?YqK{UV>3v?oZ9efj?df@oErb{P18ma(=V^d1G{CuTfUjB_;B0a*rO|wV!Ap1* zP_^y8X_CX>rE}4Ki4J@W`W?(y2kX_rP3quz*TF4e z2T2H6EcjZZ{(3h6)F1yhOa2Q>9r>?Sx3#X@2D-J59UflXvaJB2;?XZI+U_Z3Lrm>2o_ftD*GPx-G zk|&eYV2*7-WvbR1EVd2OMhLJ`&r7E8Gp69y`*u>!3kT0AAJi=)nZi1GOx`@w;$VHJ z00&75mRCFZE-m3|ZK2o0v(MQco7^VIrnUz6`JH*&GP|3}tK+icuiF8<*g}i^*T*}^ zXT^a0l@UNbuQQNu8et*7u5$o+F!kH%1VV7BT>E<7=eCze{a%(4f~jNt5$ZPzAN;07 zk$QbZy)JXT{ZL}#yry2}xL!6l>t!pw{3LsB3kbZr zOggKBI_wsm)fr_&O9ze~F{X6L(9yfDa~eJDj3GmJy_;`M2ZqLL*P$75`ry)C?q!!d z7^s!n^@Tr4Gf}?MNj7N<87CISRS~__AuH)KHt*mtL z8D-xBOlJ=LHul4a;pIb1zXeryprI})9dYK#CBuE)v?i=SQBEh+)I&xLA6<4bd_iwe zQI{X8KhRM7O+5Y@T6X$~Q%CE0EF)vY$g(p=*e>ClrGtl`HdHU-{5^E^m|DHpF~f%ht&KQ2`&~f?-L^=A< z58GWCD716u*N`(UaKggSavw||3ea@Qm@|f*%AU~+#vC$Y%y5evK7>|=oF1%%-%Dam zIs(+t(*_S2uP=d1(-~am0Pd88aj?MJiC)x@5r>Qzd4{tzfUl85hyG*mDZ}jA@N>wp z5yS1@Kzr(lF}yf=D6_P72|D$`?hI~54;ef%Xz8P@OV|J#50(pOwRqS%$>N=MWk+;; zd&DTEt5Y72jApKA?EdkI2%JLaR5-7Ff;O5e#v zQsvn~QQ~t1du4MMdmU$A)Ily-!($DvAr?HFE%t`d7IeP}w=<7;Eb1F$YuQ(9x*Iw` zt4T$s@iD?(pnriEU$|>UQQ4-dncB|`pi+qWFIO`aw+_D&YMv@!-l41BB@^X~L4n=^ za-FG?FMbQEnCSqa3hQ(M5;I3Yf7D`SfBm?~>qBl+SBMn{jw;WyMOX(Af0H~?HB+Iw zECD^9yZ|>us@VPoW&1nXg9JWTM_7v19RQB}0@aXPcMtbLbK8=Iv1mgU(`wt_C|yfU8-)@6XE= zL;dgh-N56AJ_x}uH(=;INcFNi;5v9?ZWG#}?^3<&Y%S4EI4DxKeOw*I-U``LJo+4m zTRke9;q^F;Ia8EnGP`PKWur*tCPHhhD|;uB&<8VJFO%-4Zu-4jvTL{hA@O@*w;S;p z@55Sm1*{pcZ)zs*0q1^I33nHx9_zzqf>yfXXo>TaJY`>@biJBDil%~m6%Jhj#G=dV{%ZmJ zG8$gUWL=IQV%8x*^nJdi9!X?UglMI()3 z%sxo#8gh?$KKFp!Hf1XIq?ft&%z6yb%3@zOJqq_XVHSJUMiSo_N2B~vehEBqUZtPI zo+Q;Ft-nlsOy5|O+e1AAq#bXI;2iH|k3!=U*)QL1y}n6WzWZerCB1MrBHjzed0iHI zJ%8ox@f@2X7q?|-7uRI%pK*!(bEC>*zJvO<_5JgR@{qhkzTZ62V&eD`(PnV2+7pbD zk|YtcCzy_n!)n>V|Xisd&M2V@_ny0(in@V++!| zrpe>+;?XqNm(SGz%3seG#KT)#tb6Rg7jmH0Me4mn_e*COB7ay0+X1ElnqDo7{J`dn zTaW|EX}jE8E_NUmQg1oP7_a+gOnzl08YYE_H2FLP2g-KwkiE=B73%Y25%29~xMPOQ zWFIauVj}V1*cr8xg8gG^;pCiO9F4;Hy`^qx42@=SOa3_5P!9K zu)#j)=0E6r^b;%EledpQU;f7BrcwcU(U zXMxF=i*Pl+PxJbJXi4-2D$IQkRz^u78uleyQ;oMTL1)XAVAQU{s|e)|N&->@%G)Ct zWfS=v9V|6ID2%srposrBH6KeT(Kg~}x>jsnIb-_j&I=z$dYwewMw%F3o4;ScLf?Y1 z?xCo@-lIhbGjK;%B+NS*%tr@yV@{F3*XUDw4TjC(0jce$_QaYat%KwJ`7ZCr?&=_P z#}usH!|bPBHyDeOAzx73?9Zu$AkSOanz*O=2v6x;_}}7E%K&MKiI}B15YqU03oZMZ zI@|KUT+76*BF+(DUL}2jqWw0GE238@QU1Keg)l+7oQj9IR%Si1@nSb$Lm%+|zpzHR zPIS6~dZg^C@4+8dO`^CTd&SC7lAsh|B?+oFMS@ZX1W=NTUl*}^#x(NNCHLCT`>lBZ zRfzW6R01chmW+e+_ongmibg%4f{`9DgXt9tyt-TV*;SJog(ZZ0h1*a?QcLg! z)h3Ym&Gj9jp;yR4V-)5F*=qkQ&P~|AjPOc_DMiEGF}9Fl0grU9SOF=7W0C!0hDsFg zhhVuM`2L2}s6M`}?2&?Rw}ydy;dm*-3qYGM=Nb+3#@NO{@~HY(W51iW*;g5Eo8gSC z7}JL{b-NY|h9VANidQ72m>{X56f$jbiot$E^wL4)Uu*1A?#Q(URI~N(V#8?tw&ZaZ zX#M=BQjH`%^~c0x|1a8%?t;{&3|JiOKf5Q~ay$LryE6VRBNo#qf-+E&WuE6mkYe;o z6QNR)p1e&3t*+)_g2CWguC-|O` zdz&L!%Xfj$-&eKv;ZZz%C;j2kJlvZ8@E9I$Nq_hw9=@Ia@W(uSEB#?1htiv=4-3q4 zX@?ql>Z8`KQtO+#D1g1%!hS3tzri+wVDBJvoJjNCJj-w_Lv{MN1Vi0Tmx%pz39rfo zmam~M#QgKPx(SwR;P2vdPZ5UY)0^0wjx$=FL$YMGnjNr69|bzHQqK`lX)eW2As6mV zZqF3kUE%K;k(reo9Xl3Z_Sv$9rZIpjGt!~8&rUryd=FdRn`X)cPfy}vBe&OHFdE>)T` zP)b&oL=TW`_6f_x3#wM)2J{00-fXHcrmf!0j0sf(Ep|W2s~2`_QdfdD?7un^p=qTz z=hfL0=Nzt#`ys~lMO1o-+&_WXZv`hn{y>gxmsWB=<1P?9^i@qs56hx}N6H{~arXkD zbfXhW$0X|*z1ZfoR_~)$>q&{B$y{0X!G}6ukX?y_a!(~<;zX)LOs|nD<9ZZ9Aml}E zN^w!klO3fO&o>QJIpLd20y&n6_UC>R-@ID|7)D0Pn}JO7BCOuuGWAY0wKj)5^DGy4 ziB-L%n9dt#CFM=Ig%;?V(VSIybb}dUoR?R_ zgMQ|NQp^W!j_GWAHz2Y!@`V2tVbb(JQWrao0CQq z0)8_Ki=!RKQ@N}sNs}QEW(oxR*xy0Q+mvev_p~>N4s@{+*gR3wluQuuB}uoYkcQ(+ zCBHy=JU6oeEgN^5Vw#NB`*?}pppbt{cz`KG?a-ET$0UDcml8Gg`i=3r?&aluN*WGQ zdrV`iTfX#VZj--imWWMlo`y%MhR0)%%h{tEXo|vAZCvYHncQo;UgCA3w-fcWNNC)W z*+@%wEl^3I4`P zl8uPur%Ho3YZNrc`EQ`v1@`?7aIk~C-na057qd|U#AB0c%zZ{_fG&k+VED4K>;heQ zmCm^jrT-+^C&{KuwISD|diPmjn#sb+roMn{^;b+t>wF#T+|#TcAp55d^7Eqhf%$wQ z%8uiu^DaR7-Kc-v0Yyy#rz$0TbLd6pE$;$5q}Ci1k1eZE$11g^WAv&d!>|ynR%k}l;A5`_=^!qCum=;XFRzzOcAtzrj}e=18TNWKV|nJctn~Bf zv}|Y8sI#4sB(#`L#XBIa*G#>0gAbA2=#c%uomwUZ|kf8!r#Hv*6A%HFU8jJ9{(RQoYphL%72pIkRE085=f z&RQ&M;GR|UJx4yKph*#nEej=U>o12r$S}x~MImKPtCQAL=5dzl?Pg0g4a+zC;_Aux z`>6gnnAy%!{iwy=;iwDZeV%;8wiB5o-6EkMZDGO1NJ!TQJ6^_0v%BlzHo2&FA@rt+ zHXHNB(K@?SPs2J}+Cd0>l}Lk@H}JJ3?Ri$1w@BRJ^{Rn({`CfUa?OjA7;sfe!}%Ab=&)R9D{5C=0&f)WmVM1mK(%cXij1TyA}ddb zbystZIH6Q&o)mPqM)~+TCIO2g2?a|vk=&S=h4uUyCM4c4OBpdXPb703id4OLd}5tD z0Fo{v&Ci0fNsr!J;suE|3vJpS&pU!T99pU@b89AiAAhw0UW_&Ugdzxb0jI?aTo%5N z%Uw)tDG3W_V944+0c}2AfYCs#C7IQ2m(@U2{<(rC-ck8?oc}u_y%N1P zD|0dB`$q!|eH{gJB%A%yR8v}o+Y7B>{u{Wrm<1L=$LTm}#=dohk?G}mCB^cGb;h5h zVWHL6RQYetggR+hUm$_Ro|udI_Sazi`3cs4Z*wzd{uYb5S9W|6N67?KJNR(SH#!9D zr7cw^-tL+1q+u%vCtX&YaM0#`F(C{Dm(1_HEPyiY=}D3W?wic&?W8c^gz7oEfWB6) z%RRlm%fhKw*1N4E-L_=p%aGKdO+??QoTSSvX?lruw1{-;h=hMu>k_^y2${>I(i_A0 zGpy30x$FUf$<_$BU~gqOTjQFJb~8chvk+tQDVv#9jC?BHE_>l)ZLSN+spk`JNZq{d zLJ6G#ai^Z2kEYW0Z8!VVxz1Da+VR?>d3Z1Y)Xix?gV* zSgq0*Tm-Zsualuc;Jj=-%d?DtDHc{SI@k@rnGNq_RX0XonUj!sr6AnS<=WpJk^*-C z*8>7vyA8jlqlF(u!7Nmi$+(6=Qp~46{-9Zf=F9Omf!$OJk63rzPQE-hb($6dCL zH> zc^ykYW4z&{K2AD7rO(hecD2OI55_t`)4d}{b*1xjaUjh5lWp@!mbDYB3ZoUrQVtG@ zY>ak2X-3uG7oI(|M0Kt|v0qd-6Ll-!ph$%z5K=nQCxCgQM7Gx2XfVH9^K!W@J#b}A z6UoOSB%OFM6tB6&&=tqZ9IMxk2n@|LQFBUM-Cg^l9g>0%9Gm+M@^kzEKz-Dm7qh5K zKgo0hvD5MEN;IEN*|#_yq!~uF>_ItbFjJGr@3G*IwxD% zOLi`y*fi6n%R5bFLW{jz5`&SKUm=%` zmH^=$sG(6b?twxZgBjM-V@b@~B^LQ%H%znRt=rj6>Ks-8L?xZY60gLfu zgrd4*WS4)0$9)oC(Nc$+hq(n>?korX#JMg_cW+ZGGwYJ^ zHUM6E&@FvUZx4O>3*moqS^GZ0G7Y>#WEWem5UwscEdD(K#FB074M{bP518+%&d zIOZf~*LHiF_BIRIvUzw75~b1`FV99&>unF-?F{c9A?D@n3?EF-Kc&n|)RO`E@5F%N z@}wg^nMnKvdN)?gi8;L$K-CC5%~jH19Km#6PpX%I`x&a4R2k!Qd!421*OUHrmQN85 zHdILE+_pNl)K}{Tek8&Q-UZSdH{%ahb=AywB(V4+e*|{fCM#)#eh;oic*Jsyt_8r| z>B_?eP-1|cMU~al1vXWkR9G0kvb*S(?d>AlvKn{p@z$1(Ux{>ZEXCK_5>?|>4uNy! zMH~tgr2R@(i)aKdd~%i~qpA*k_w_hyfS=OLyZ2nu8B^=-Pu|{U_9W$dYhm^HzGczm zkIXFFTqV!Biz^z;hq%45@vn*k{-`Xbc)wXOsJz% z1duBcKyu@Ma`t6@U)muK+xTFd^XQz8?_6grGq&I6ED3YWE#(}X_h9nzRV--=4v^T2 zdKqupEf^Jg#gQVnj(el;~Fu3PfI(n`JliiQCjP(8NKtGVoNN*z-f*9!s9g-R8BCYDdh5z*W z4q}H^anJUrXtRA8Z?3S<$Wz2HIR1lXBMT zllGiu0KdQ=?6U;$fY4--zN8}kGGM7my(0yF6acgyx4w1&bR@BKblYXKUF5lr2AMLP z!Hza_JDW%CK;DOhU0JrF?LBXM=xh%b{JEHmU`V$8_Jjt1H7NIKsr(wse>yql;PTJZ+x?El@HQyU$de^F@F{6 zI`@zzmd)Ga2eu`r%mXBYaXq@-b1|91+Ci-Cp&P$jvwR$$Mag(z13fOBENO*}Jk_BL zFW{t?$!rg|L6nL)#e~;)GM|l){JW|?4O8rE)kNm=4Q#6Oao(SndB4Uu`W2~qRFZ@Nz^G}Ad<}!t*zE>;{RdAsAEV{4iy2OZ}yf+jyb(7g1$DDW;>Z$ zTUGQPtl>TzDrxVDtIb}c)2`7X>)|$$MenrSED&ZP>vTlqLrhJ*Zd?@ix#O}5>#r_z zJxKkwuNx%ZXo;t`df(`m>Y?!6xNqpxu*@2+A|FoH_Ztb*7+}&IILW)=95@3v%i?4= z%U&Es$XI4-1qTT3b*k9RLyJ-Y&~-kOd@l-8cJl(S+Xnr2;SBLii?{7mle^lIre)YnR_pGTy*c=o59QmrVB zAb>^GMHE@QhtHV3>H)LQrCMPLJAj2Q_?%-NX&4(`6Ikxo&J>pIL;(HSu~nLpn-9M3 zidw)tK~@t~Fi+!{=wJU>{MX3MVTm>gX7yw)885;5^Msw)-kn6@KDz9EV4kh{nQolj zsr!K6Ex;qu*fmxxhjRNrLCGnwdKWY5Y(7l0MW>7ofqy&PssMEIgm)pp1CsO=C4y5jW=$J1-8Mh-K zA%3(ytku)M$o7crlkat}H`k9XF)zle`L|ID$&D8DRom->mM__sE4SAY1=Gv6Wqn&M zwIJvt1(&764_b5*NTCgVvJZM+K8aTq+dwkxZ%XC+Wm!>lpvt3OA9118o3K3cz|12C znV4Sldn<>qooPIit28wE24}JGdas^2ydw3#X(4~GEy%@`d@wBJTiORXd`#K1^8Ue! zZD^~1|EBl8h2$Qekp&w*1tEURCZAa?jM=kbCkPFOxFgU(@LyW!ZrvVqu!OIagjs6( zo7a%ZqQ!}mzJ(8kd9G~2+{-uR+fp59FH&PHlo zb=%Y2C$VZvd6~IFz#`2!+_b=X(Rp2MCv@rd&_~N_vSej=y*xYzQ?`X>O`|!syJ_v{ zuEr;ivn_e_HWv`f*V%l7`&-`>tl)edMM9vg(wa)3PTIq`Qvz&h+z=`5cF<|Z6OxWV2D zGnn@+qeOX|__v0C z6(I^sXYcMS<&b|*d5w}x zV}XH7Ia3V(R`bB$%UD;CQJr-KF=?44T^~eC`OU1XS~uIW8iQ!L{PsClNDq^guwV@V zwq_AAyf))Z^B~TrSx*WxX+2+)KTg&pxOY*?Z~tz;kqoc!kC+z5nr#so%99jWb{=8@hgau6jYc3jFrzn;$;!4P9&LxNLUi{!F&ESWf6iAU{!kV#4C%iuaBJlE~(9K@RgI;0} zuP7FNE(PbIj91B+t>Eh~NszXS$&AC~SW$B;5z7h$e~2rtOYl*t zx%u4OTzbbcHpz*0;@gJ9%7U14oWW9(i|j-;bZ*n6id6M6bXZ*@-^*0KHk{p%4pJ9~091yg%sWsJ-OULA<;AwTz_u&`5){fjNN|fe84(h+*+5fULIa#0JJWBNam`PKqu74(TcZ6}W`KudoGfpQtyfYqZoK(QK)Ii}ptJ!z z^fOP38Ie~>w8CO!y8PEupwQi%3pAya1Pp;SyC;i9Sy^7l9}W69$o*QQ*UJ5hwhI?U zqMF!!S#^3%sTn1->uOe#(Lm|vuk&nbZ!-gCsf0lDO3CECar!R$NO>*D%&O)!QI|xFBv}8d&6~a+ zFyec%f;p+oAjie37#rkn8@3iF12U)J9}C5?4`yZl5N1r}@%58y$@|Ei+*15y%CdD? zWqpu7QND`(%n6(^f6%74yGNF)TpXjgzAMWhlzO)j%O-tlNZ$5zI8>Q6PY4I&$MxjK z3%YU13R~=kLBZ@X;?Mu-C%~;`Pr%8^J?6HCly?2hb7w+^KT{b2^N6ygM)v8+5%ypQ z6prQ*iWy~_XFD<>=g12wKARD@Nu8B;lO~^X>m#7bs2IuzIk#96LnH%LWV(J^6N*mnYGiUM@`M+7^WGK zIk4UdW?`a@V7bVq%-LlQsIlzi3Y#K7ZZ09yRu7ZifVh>E8Xq-UkR-aSh1B@W;pc_g zzWK=Ot)#|hO`wP{OK`51<^YMKB!ljyB|KkcFFvg((I5OvpVf+Uc)ucYP^L0-Uyh4V zf55ta*AV-eDP=^f$^|LQ?Ayqb z?&dG#u(v-WhoEOQO_kc<3!F!wSr;j`jYhDU)u>CY`-xV7h+P($hFYHk5#??1q%4x9 zLkL$*;Mkw5g00~m0_oa9H2#sYMmy!q(<)OV=XD^#a6wg}EV4}AR8b{ttOpy`gb0C! zSltLhVsl*-ORuDY7V~)?6twv2rx{d#JAx8*nVFeibW{c3%uK4U@&8PU)|Cz}9Y1i$ zh|-}0N0$y6s8Wy39;()7@Nn?3VIzj9Y4{}gw%LP$wr%srs`wdn7kmr^u3};EA{=L6 z`Ow5ziKjzG3>!Ao7V;ZC@RYLBq5qe?HvzAzI`{vRv(7%8goGr_DAE89Rcvr80 zwf5d;NCIKdw)fWOk?gaFHN5Lx@3h`wXxL<{@3iu?ynI-AHynnmjv5RiyzQ4kY z0DIL0q?DnBs(&4pJ*24M@F5dMR@%aWAXr!>FGji?xQlC67~Y>%>oOnKOdll`&y z|0QJ^!!N0r$c_s}6PB)|eBzLkPO3aj4^%VJ6|babVrmIT9*hZA=d>XcP8@nd<%nS; zhS}EWBegCjlN*~}IJ1juOC50Ok4!g%ij_36TJm`K$chSEi<0KTGLk^(PeV@{`6pW`vaA2V!avy{SILr;$=lsD#0e+Z zVXCw4j38NHl(te!ee%#J89q=)4GBwdYI;;}wdFX4NE1c`aLvjnwsr(DT)%NqgDDv; zQ!8*q(kpPGr}ZLXy=qowlOI)%N}bHdlaVXWMo{7=e$mllN5C^aO}qKV#gu`^nv-_Z zmOJO>wsaE4)O|)47wrSj-AX3az$7_$Sr|!p0}#)Rg*83v03=!2<0Xw-G zGhp@nLW~<;Y0#h2KRNNP1N35WzyOA7vZ8YxJ z>4|#z!m7T4@idd|q|<1CEuRB*i&Fm$Dnw05fKK5Hmw|}ga^u&ckJOo$$uy{F0K_=y zibW()L5|~&Su$0r+I53y#QB(mRc^p|_y-`4Z zpt6Zq9NM95l6`gndT(@4Bv<_xP~|Y_e^D2WrxRrr5^h^(JY=eKrqD>-Mh7p;@%0@eqMmG|5Y|h6bWcw{OCDrI)IJmS1H|_BnR@IKX%x&bn0EDvu zVOKPv;W8TJQo*;8wTdUG;wwWOTjOz?Iv0EY_D=w#@F|>)_*lRu(`IThkK2o6MQ9$5 z(Q%t+vB1sZmB0Tiz=BfqTo%jm((v{#N7rCEo;0A#VjJ|$YCNq#Iq% ztRws7GwmKR)UGnq(-eftu9k8=T26E#%i&n=T%?Q4qTx;UsN~r^IUpCA z8!?~h=;YbF8vl1WeCl3HOzR<&1MR?VKC0Vx21N2gDy!wqR90P*RO-B%E%I=jCpzy|ll z>|qTG;eu|^g4PBL`l>Q{yY>8D+w0k+^}J#-arUg|Cd$@mLHk;`y#DuJ(EI%beb`^n zdVfJ5_7}9iLJNBHKh1*Hf+*ky;qrE$b;sJ7xzDP%wB~&1*1Sgw<5;zeIQ2kt&X_Wa z2j`ei^K~*A{AL5^`wX8GnUGiX$adzRUI8_jf%ut|J09cY#hutzy(ACf$|CvjZ0uqD z`i!7(_URlIta!i5+D$INsAa84g(;%zx!qOB58t~}eNMRxeZ($9%D63%h}F$vtr~{t8fe|4l>S`;(}v)y=7Sn&%A9tzw0P6c=YLr)I{m@A~o^4DWpii zx~XE8&rM<1L16F1_+&S-;AAEBy8}2a{ui8*a9Y*^PS0S8>zo@uf)b!tI`gbR_~hlF z+VgCs>JEkIU7OIkvXBUs8*GPGJ=0v2(Z;)DjVqKb!j#gH0Mf;iu=2fWn#_0~87KV-RqUz)+#^B_>prWkJm709>g)qi^%^pp`y3n< z;_Ru&3FmN1DW8xfO3j3-pnZyir?BxVLM2+oZ6^vd?vQp|isT=AEYeXTa)`6fQ|z-q z5oxbw;r^r{|AkV@+@UZ4zB$?eJz@iKiJ4?mx~z!GEL&jymTkQN7)*)NTjWMm>vgY3 zd(rTIgu(>1WY!E*L2W-`r1b=UDPum6cqKv!p&t0R7u9$@{C`n)SZX`9^#Q@$C52#q z%1|}HbMqJ}56s6EHD2s+r~VhF z@&~*^KO@!T2A3d=PfUzADqe$JuBRd-PK@l6t+CxqR#-uLHd$dU_9F9qAgle>aZ@|c zLZQSF*Il&Qw1b1$d`Hk$V1#& zPQa)bytzm~o%ph)UOX`I$mGkjA;}I_&k#TDWaG5=%0O;7W4uii91bcZD2=*EEM%zZP z5Ri`&F!w$0$GK+KWX0$rLqfY^C;Q;Wlo`;xSK`_3psYB!^9h!&Y;X5m^TiYxZC@O1 z-lKvC`~+!{&hi$d%a~1%~lCZiV*<;B|c<+dYUi z6C&~-<_?0H`@qkvB=Q*Xz{SNSwyqftA?(gQWVL%3ycV45d2GKzJN7@b&sEp=X?&k- zVGAk{yTTTogHE4;Q`?PldjW+*f{g+*GqZ?^&M`HLh>?<-s&w;34)ykc;8wNOrf6q# z)95DcP)m`+j^DRpSg%|T*PYo8)xsy7f?qq=qO+V#{-%s@XH8PU640wAw=~)r67r$K zhYIHcoL=UItU(YbApgE<$n0qrOcl_2QPEI=iSJXUD)nB1?+eYfxZ}8Q%m3-$0Zfwc z1SM#^Mc|E4+`Y^~q%XU3!n%Rqm&~~*j*Q=O!Nk3_aw#>3fjJiG%?NG8aZDM+Rf4}| z(S0PfDw=8lZj1Y{hJKN;eL>5O$~Ai16w=$dSN6fJhB7iR3r9rdtV&>_S~z=&WnUgGCyJ;$b=CRiW}I66yVdsYipf+!H_N8hBVs+G61fYg zvF*PB?tUMXZLqH_o!UUhkLi`mldm-LMjx}rzOis>;%J1HGNt_=G01c-Q$@k_>*x<; zDSbX0%$|qakS&k3e=R`zzIivI-+jznrFH$)Al^jQdN-Tx+Zh6S8)UJs8P6kyupLCW z1<8;AqItMVFcd)3U>JG^wGG3-Gsx|)l|md~_!r_u1;Z;UYM9!0QYD-Myea-a4kqm9 z)_}6Rfbu5JL>4GZN0pk1faG` z$1938aA5V7%Ec6MBVNz7)Us`9syoTbrG0&AzqIB{2Jc8IX1J}AQrP8FBTJ(=56|q> z6l_|?(8C;wsA?K@8i0*GRcLfIor%;nU_& zrrr6Z`Ky35-{DB}Jw9oki+v==++j&`@}48jGkns_@y$Q|JMAIOnspW=jzcWV;#AX8 zTW%w%Wxi!H-B~Pko|1sw3^`%@H4@dF&u-`(+r%j+QN|^m|vMlQeuAe>DZF#;HiV4Vbq#Dl4#YGIvor}Wkn`N48JO$N6d~ptg zX^J?v4Z0T_5a)akxzN0iFbAdNhyf*l!QKfEXou*}0K1qcD;lhM=CY`Yb;@9$;C^58 z^6(~}5`+VQrURfZBvHV?sFbHrZH~lR1A>zkLzY#<9{-#$HMM(b7vS*BDGDQi!c9{g z%`~!+(ihWM5as83v{iVk7<^~8_YxAky8T@c2z{FY>H$*uI1*5bvHh^`H?kHDTd?>5TI}l;Lo1_fdSrkPeoXyez zT}F-BNW#Zo$(QDYk22f9yj!nVb>h~n`be`$ZNX@kW2?|?H%vjT zeHoVML%0+o(8at+(Mv)2wu#WdB!m%-e_3JMHPv!< zGxJZO<<}=9SWUWre7CrEXK(Z9Dd5ZNjH*1_2yCv3kKr!HsSwwI^f%zgb$b6L- zuZeTiv>b0{x3D_pT^HrWfoK4mF<^A0n=5h${mv`w$7J%%6(gW_gXIv^#3Hf@_Kh6K zaqe%l(dcYur$7#8WGO2YA_u!>w1`nputSwPgd&G}aYmmHTfc3LJx1Wdo-^!gUUE0g zHTiJC!@VCoYEL2<34NuWO3PMouaQwLpR&NhNo)c*-#BbZSo}~u^Hxxkng6mZZ45$B z4EhK1F+?3uv8^XO2hXcx$wlrz;Gt#5)@O(bE0*Cy}3&VTtl*Nc|A! zbb%Z`I;{)iFx4Y#!=4;_x24@ETgu86n#D?jXrAKGUJq}MS{|)H#{*!>^0-<*0W91FMV~CBJKXxOYo3f z?4#NBKW(5f1hp^o8}ua)K+aWTYN4c9;){7mD@*g$3*H9)^VMVO5h1h=JiH@c;o|E1 zO8+G@bBwZ~*)iLQP&;Be`BaEuU%evplUaOXVSGN2aQc;FgGv306Vr5`2Pnr@Ri8$? z$=<50ce`mKtIBRlMEjdF@T$-bTHB#1WvBfOZwcYfQ=^;P7>2LrZOejdpU442aR*OD zI3RLM4RwF!6rE?mDXt;Eu{`~wA*ZOPh60>ok*Ji-D6<@w$}Q9Kj%=t$ktVfqURTx< zN7J=Ez?EZdV#|ZL5_PW&sJr_}xK(HtR#}wT3O99BUSjEoBN_ry;>@U5RJ29xw=y;x zwtf(MAM)P>A=5;*eN2{=d^Y(zVFRM-UF8TVKS(X%Lo8vs;IzS(!JtYS#)R!5*88Us z>j1)KA%P6Q1ZO4Ogr$?T(<-?x2Ln0q<*0V+gfZzplt~!dBGhgib(wf4`jbYmVWwgl z-zvdg<&{7>tIdTXn@5|sAybyFESy9PQLCgo#Xc=C$v({<5oOsS8t!lG_e8@lAeHuZ zlkf(AQ)rbJj;*ts<9g!cQ#NcsZ2|D&b`jfJNAoQ?-gz|NC*Xpj3}icnfWyF^8d)ES zIN#ai!Rxs?r!&X0NajF9344Xkde9(uz!Z|v4LQEk#-j14Z#rAwgvKsJ_}K0SdDDrr z+-t2z9!Y>p(*4+*0-x~klwk+`*l{oz)hr`gs>izX>aeV!DW@c|OByRU$NDMbcyhX% z!|oh?yE&{%o5P9P{T`%0n1ZjENo{S-?|!SEC)V$NYvVmHH*>se7%y^SE-v6of_B>& zNjw(+OSxZQzELR|L>JDNBuL)kgqTl5A*L1SAQfQ|HRk)GM(da6OynO3UfZJJRjoM5 zl!juOPxr7~5rAmLqYp#LB@nQ-U0i98izYhBQ^<=J57y@T{Df^49+o->5st}`xwY17 z#s`9EQaqBt&k39px-t8izhJBGW}eB?G7Lt)mHe4-7*!g(y6^gXtB+aNsC`W)%+n5?%-n+Y3@0e{Lm)19PLL+v9y^82RlCz@B41lSL@CS;48Oc;pIF8H*X*hmFUW zwS++I$YG{TJh5vtNYgn**-<3BTyqz5hYI&M=T+guNO(gjYipG*iO&+^e9Ee3KU!gjw6Dq!kRWD1jWT#J2g2eGD7`+{)o0<5hcjMx$W zjlf-{;o-h-@BneTQ-kGtuh?$nBNSY;9S;dV+8CVZjaRm zP)}0uP2Hj3_h!(rueq5c|G~cA-sa_D948cT3~+UKDJz4xp%iAF%{$0~y1%qgnyj>? z_$ucNR^Sze06)J+Hcl%EuaF}T^T8?BJkCjYku}w=;RvS-Iu)oJXRCUGG;xV5Ibb?=k@U8IhjH@ zmY2xgq~%!U&*ZMOnZOM zAezbLli~hjpFb}*gmYT>{}cv%^XZOM4#*|tE#6G{x{YfDwD~0S1O?m;N!z+XdI?Fh z;fK|EsUNN-f0o``B)=qmxE2-1-N^iKdD@5b{14v=KAh)&_)bpdhffxz_ISo<*W(v# zK-=RPqg{_*bn-vcVMj2{o#C5V#QeS)re2Ah?4@mg>Ci36CMVpOO#+A;iEM610*KUH z4`KR)RpZU*uPw0yO#tP?{jP8!xq?uu6P zP7E8_vfU9wTLF7!0*-L;ZBmDpjVMaKmAkE_qaZJVBK%vvofuMaYRkJe)kK(0fSeR# zY)%7`7^DXDlOv-1zylP_pG>mAD`5%>(ovNE@F4#=i#hDHA;a~HJQHJvj5yim8z7lP znDBt@kr;BSy{#+{VJ-#|J@|PINb@ih{2r=hS4+uc5K0x{(n2tnlSZC)+DMyAfUbs( zNKIqGm`+QkoM2{Y*(vy)nzTWWUFHlv%Se_%H-26P22nnIRBq~EtcJ#uD*Bc!DhD~ApL z)6g+C!3mf5P8vGgBDF4E0v)}@@IiP# zrv^X#?zsvRkC5^rY*g4ArY}jNGOxH~DMSBs!ikmTYM7iFe;QUXikvUHQ|?xSG#r`f zM*s+^L5BS4Pj&*lU=c6);p30eb+~2cs$%3xrw&b|e*)Dw)lc-o{75@ABr(k4VGnN#m}3 z5oY}99j+i(z~@AV5RjA#f17i8qRU9bD1z+^9R;^tx)p})bB7cDG$b)Zkf66LXXsfY zX;e7}BFL4E_{^9at4giuHa*FT)gj!i7xYP(CnZb-@h-(2kct;g!V^1>^sXG>n-8i= zFl@{vS}w=*FY$I6>h+nUL#VYA{UYLPNYWO?4sv}gj?{ndIL=pXe!ex>6?7k$W$u&g zK>{^dR*StpH+s28I*lgm`Y}=l)Ajq4WQtiiu@>9dy&fKg#r!DavYNjXlCUx4w%M5- z)a;_Mw8v<8K}N95muA?PZq-Zl{X9u#Q~K@@2%E3DX`k<=JukZkYfmpyjX!Ifd~u@E z!Y=dxMzMKuB8dUrAUZI&1$J(8)8?%E^X>hM{5gLd6nzVOTUT0Gs(WU)WpJ3Lt$^>W%v)9p*Q`O{sOKHYdQ-Dk*Clb5>+Uzd9F z5Z3E7nbZR|=jNpz*5TO`tMO*3<8LE>oA|3UCfo*J!F{u^((7I+_gZq_ax|@*c3zL(!*0|`=Y}j24#vuDGrYgaxnCz_fFoD2k{09B z&Nr`TVQsq9_4S$(7+f}$isX2UWEFb@IdQs#bQVA1S1E%T>gDZ@7VlnuN6zN+xJG+> zKg*A9jx+NB4$WD>{yYG{g03a)U%_CnbG$!JCwau3(a|~96N-n=N67gc|8KAJI4VqF z^UE`9$fE<+4Q?Q#1_QpDj74$XP$6$7^MWkx*SefKFHszoC)Z3-k<~8drASR|onkZ< zz6tm~GxT&eXQy#F5xuC`mxYvqWKCX@>+dyF8svAsS#y~=l~me%@B{zJsx46!X!cUf~%ws^x12g28cyGOd19Rj&4I?vxq$x^sWJ($9QT6|`;g*-D?2 z-4J+_`Vt?gs)P#{uMOJHk3H-a4zq246yxlZVJj)QpELe zP5fQx2lt=S3>LFidvRIHRg_KvAZZ}lLn+Su#`6sr(+wFKS{|{b|Lm5zwmtP#4>lN%eiR3->2l79_5c9FQ>=rwW z`SI<8w9GhGXiWE2*#a{w1>buG-{slG&k}CSD-8QSy|RL92KI;me@_)1U?Iycn?}BB z+IJ5WL881*+r%_xAAp7ow8#G-=^ zZxMbWoojWK+GueVv7%_(ET__t7vb5kTq(T%OBN4y#quo9Sss`gujWca#^AVs8BR@* zoV8ItxiNDJv;B=NKTx7IRAc5r7*(<@yEXPzS%5gh02K48YOv58!rlS%3S-pI0j&>F zon)?*!GoTk)pov-yiER!qGx%Bu|-J=iYZLr*2&tgUEG5orCh5Bl$xQEPc=XF2@_qf z7Z(8i?{Qfk?X0N(N3nWopv|MHfYT5a29DDJi$E)#B-G;D2vK3f;T9;noLr9i(k43* zja>F;B|YhEUiR>d%f-yL9FJhSBR3Zlq&MadUj9Jm5Q0Zt3OtgqmGf6RK(hdDt-!i3 zXu`1raW+a!4^mvj^ep+A76mfXEcuD!S}J^+2h#5f$+Q9HUCa`_zkYHZ$8>x>vs0-^ z9)UQFOo2FYf$syL)TzQN{D^2Sw+BDe(wxu8{jZe#SYIjryNl_J-_$^MBHD>?)I(BH6+Nlwf7NZ3-$ zRYSD9u>Gk#CcC&|P{8?I(F)vldHo%73-HQCkzy|r@8X<|(lT`vLec3)PShFV7=B$7OGxe!gF9w(Sn@jILc9&N5~W*95NzhNU5L!3!h2GD^Is^< z$RBdIrM!PkA5|D&x=S`k%#nS1lC1;U9W(ve`G2%aHVrH>q9&+-EI>&m;r-E+ovy zQO&G_(8o=0oI?uCM7Wswv(7y|D*7qzm;li6H1iUS3M|}*lZ@Y%n0-o>u7gE6vcc;! z7&a>_GN8PiT2i~%J=op+8`Yirc-@ci;&UL73VM7li!1^~CYPM?yj)Cs>r4&}UVtFL z$4yz~CWkFN7q-uiT#-e$_fZ~U1@c2q?2lfzO8ykXTi1Cx;NMcd!f5*u;)C@KXd4Qk zVY{KAu>ppDt1a0rU+bKx;R=ruves^}2O>PBph6&qT68bprC-n!=i_w=F0CCFs~(@tknJ_K-oYyp8DK!7kC5!+fGv*D-N+B!96+SEqT zgVV)JzN`{KCMZ@BKWug+)R3FQg2e309KWUwUi=s~V)+T#9fKE<56tl(*b{nHOuv+c z=tar|=*Hb7c5I+h>fN}DZi(BXc|4!xn8bfY_Eeb|#w{cQ$(}+m&x1*T%Q2_+eFY0)3C647k1Q}4{syLMaz6R2XZtm*4A4MTq_ag z1sVX%hm}aM{&|h!;t!GAmuK!ExlC`S_qYVEpIfr|V>&f!7DQYNMC>Cg8VdG&vRs1n1FEiWx0Ii-aktbnXUVsm0J1m> zib|-e1K%FOsq?MyRBOv<>RcS$bxgWXK$2FWOn1+X52lh)7q2VXQdM*&78}dQQ&q_d z?OvKKxr-RY<5^`kJ8blO{^z|ohy9h;{dl{Eb15Z5F6?ka9`(x7ba=w zC-xif$~kWz;e-{ZHe@$i@#-@rtbZ9Xx35H&XEg1b`Q@vqA|=G`*P|aHX7-hf&H$aj zdFIIuH6$(QE8%DfsR;U=@87K+xrgp@v$*{??YFB*0?`e}$>$MdCHN_chXlW~Nj#f> zGU*19IQ?N2Yba)b6QR>bVw+gV#?T{Ph^b7t z6!wF>i!PN7IerH$>_dnj;&Bf3{O0if>-PN%MZn(19Ko=WXX=Q`J;1y@CDJb&y&{Wn zcsM+}2HZ6Yq{t&CidB1TO1ZfsQpewV{wlDXo%~uA*0O`}C;&*bNp6vZkDeEeFSbhO z*F4ltn?9kK7j-IbUmO&xE=ze+Fn+5;S*)x~9fBg{yv+a9%j+#bo8`>Pi1Gdb`7*VAEZM17>op<_m;1hyl8`WQC0wP}u5MW|zh*WfihS;A$4954pvw z9c(`$rJ9B90&%+a?D^eAtltp@Q38iOWQc5AxGtDk8U?Oxjht-lrUguE6j&sW zjT<`!*#}#PrCz=jtii3zRa%!<+9b}YgWQQ^hIOg5m~J-6j?@ML7F!d~Qes%~tkl6I z+0)j5)9RaTLCg_g3#X?ge~E9TWlOhP17fsUGQvYYLV8CA&V_Rd%SoiqG9eGvVd12VvIl}`t-60Dyb zIVwA#1WEzk6L3Rwo3VdE38XWW*v)@(qa_LZ6#FEhIv@%5n@6#kmHX5HqVjM~ z%QNqdw*))^;jV^&H}hA~WuWyrQ>jvTurjjwH2e`V1q3v_-lyTu`!xJoX!yaH{}1x< zNbiPZTrUe7A8w6|i+xz)s+7wXd(5>HRCBAddBIZKgZZ6+L8P?ovxj=0 zc-gkug91YA%eo7UrNf~-R+mmRwhQ!W;x*2?Ta>P=p(B#6$?+Ug=Wk!UcJgndm;v(c zwmveNMCKF$yqoZ>%I{C5#WDJ2Q zO{~c@Po&BdtqO4Mzqw^muUI_(V>Y&|0+-_RvNvhzz!viniHuUkgb+R5aTq3*QY#R= zqWo8=3?maxOmM$?l{p1M+JO`Z_Yei51@x@60DS7nr*5q{e-TH$ALA9>qd0{E`nUGb zDo%`maF~oP;X!r-TVtA%Xr_!OMWx%>B4N5@oMG3o0Q^5889Ok@EP#rD2;M>>P60)_ zs%gc%?^baFcfSoK0S1z^S#1dvmcXMR1bY@w8O(uFY<*1&1V@Qm=`0_b(4clYu~hlF@iQX__~x`Z9RhQj(^yl?X*9aO zSwiAXnb0T-mS#h97P9JgTgdk3)CN;;dcyOZ`^K?1ITOb?HXpaDoFi@Q zQBJj4uNoypf?&RJ>4%*~jA3;ob7o{eRpIJl-XWJ}U)=MqBGa3Gzd}`VX=x_`$7YPo z{z5vc{D}&RHcxIOX*nf$n ze(z7mDV=UQDok$i(#b%dSXVlKKI)jr`Yp|;pY6AdAu*rM>0|Pr`SZaF;m-%$-0FPX zw*Z{^czemP$)aJa#CXZ2WtZD#xl|hRZ`pGBTW7fZl!cX-2Lt}ZUY@JVkL~4oy8Osq z&eml!mu_Q))=JBCSL@-@77u09+!m9jvuE$9NmJ04{FJ10=^FPA8#Apy;!a*(9LJic z94|p1Q;wIQRDo?%q6;RC0oKU;-B{{?LylRYK9O&XOrB$6P!$FlD{T z9kG&!Bf6t!YbQtcm6@v#j0`e+-c zNTWlhkEh5t%vu;)wT%0Au!dIcV0D4=+q!AoViQV1itPq0$~1)4d$Z4Up8kWV!}vi&c@Qf#h=)A~5P{Z_INmo4O5*mVCd z0rfHg^_qVHHO)-EgLW~_Dgo-1awkPoKS-;{>;BoYJ>QTKx|lL7>M=oe_}U&dv`KdIM1Hu&=k>zXU_o5;})4w2)hBw+4bJOM>1)IRRVhw z(LUB+wyG|Ez^HilnWN&n`~M#h-<^!j9q~n<;XC9FirThb!)GT8X|oCk+CFj?d7Kck z93t6n!$ov}cLZ^V+4xtQyP0J{OfDoTQ!eEbmS+JjETg^7dwSi+vBsQ~cJ7CZkr_{Z z{oTF(<9VeR_xne@&foL8R8q}|@QY$9$X%%s;5Z6tq#1KPsip?tyY>(+L45`i1*F-~ z`mNY>XuJ3pwOyynF2tk_@Jb$K@oYO@=O20fm!L^##Nfg6S#`F>`P4>a2uH}Tlrh9I zM|5-Mh?eO!>WdIhgtw?6R4{IC&@s*Xn6+^HxkUaBZ*YwwaGAE)Yh2Uee3l^}ciUHq z1=8=!{okA9U&(hD;CV;gDEsb(#75~|T%3ya?p*)(yVi}!+^CK5EZ=desmn%tX}vn1 zv6pqa`&WCpNSABv*P5P#yzZy48u@s7#n0gAidTVEuULG4tXYE83{%7= zdKqI9BX|(1k;NbV;s*x7am`-Qp58ux;8Q}jieq!k_(X+n{Ufi@X8BW`21EvyGxk&i<28Qqw7qGR}!h{!f?vWbJPw{ zi#HlPs)y*E$bs3k#c52;6tFUDXpwQwMhz_*#TfgDRH=X_mJ#b8d7WwUdPer`B5P+! z{7S#oYS(Ck*CoCyU%iR9nYEQbC?6DAde9bPAOjvf6|MHVK1<#od~eIlYE>h+TXShp zy*7>xR6lU}m8r=7{+GaUvnjbLb-hg7S z@1Qo?ZuI*|$QyBaP*O4^Qx8!Ovtw@jXAn1>cZ*{Cu_q@tOL2cfm0&2%P_GZ44nKsb z1tjhT)ofl!(pl)YsrNcT4zFfbe71qC+fXa>=;WGKh!wbYn7Nu&EHsxP3~q_?&_;iT zWqxnTgd;&^0SSU$q>!NVGdKK|Oot6GqWXN6Lv%-m3e5^rZZ4vnZBO$yVNN~p5i7FZ zv7M0i2!h2#`Kc+jSI6keJ3QeXtl!qNeNHwL{_cpzi&_Vu4AFQ=m)F=!3X2NbApchO z__=Vt&hY~o@gy6rdSz76hSnAXo^{1{^AlU##4L^0dcc~!o)%M`6*2Las@y39dXRf_M(uq&OITIZCu?1vcG5c+t;b z#FK0k(q@{Bm@g@;V_&3`cTi-PhsCF=rXm>=|n!ntK?_ZpU$VFb>x|1_`}eC|Cb7+WIC)JUJjGLV5vqe^sXM#cp3Z zvDrJ^toF)1VjGSt<6mxBRmRp8wHlK%dJFcD{aaT=cmTM)B`J-nofk=Gzd(Cvoo(u%`w0rtC^g}#6; z|5k<6zDgNX3GDMHmJy#CAM5=X_|Heo0^Uf>6&3M*@x#1chk7}rwYvZnT)uD17#pw9 z^1In(hb`g`>!#phNcsiDC_*XTS3J)gIbAc6&$DgNR#lVs_`Q-zR$B4oK3%S`m-jQ* z?mC-Go$EO|o?E5!2j+U7%7J|Z1E7sLk!mj<9|j$uu-?2#ss3uj7e&e{D(yVqr(%Wj zg3p<1W7EtnUcLSO^u*!{;-1EV#>gPd;8G-#gOK0z%Q%LU(7v_P55anYc=~#m&2}Ne z$9^it2(>7PJq5H8S{Tb!obLtq4;08==Ki6C@I@5f80qWk?e3crWuu&hNZZ4zAx)+y zJ)8pP*pt7oGlG$Nv7czBe2^aZmoQRn{%9+Y?#g>Z3Av7$-D*fnP|RtcP+%Z|r9ki3 zwhsILF0y#D6sBiTaC~b3FqOCMz9NdYScTmP2tZV#=5Wo-B2C4<;LZH;ZilRk*dxR6 zNUu(xQg*5Co|yR$d#b2!>4Rf3E=u*+wSGvGx5w}3W>74?$?BAA^6l9%IwhE@v&nsx zQelvSt-6prQ2q8sNmZ?|PUijYP;ym?+E-*@rSe?dYmYct!jOjO%AN4;o07`+uV`r9 zDy{nZSQf3g`LaSi>R2%FrpXK|-x(lhD9@57MEdgLdt-`C0yVLpRh=xAVqmB_cV9OiDP9# zb{-qLh~+KqwhiQ6*}(;rysQG|FDC~1O@-MC)W+t~I+6y%J|bbaXD;AuWquE5VWzE1m=u2p#yU$#IYh zKFZ+z084@F&^KWuW9MsX0Wo4}bUtmDEtp~*4HY6JrS_A$RIT^Z>N3ex($=hhq*>R= zhvkd0^{&k`O)z#wyK1apZIOmw+cM)qD`Iqv*Es6c>ZbXDzi9wXqC^~gaYs-y+&%Qj z?y0&6Dxuc>OsX7*NqEfLzN>UpAt-N1Ma{|axk9UaL|zHQ`0)NDE1AewDxa1 zQHJN(cy`q_GP{j5QDNmCaQtY!kk!0~2scOOX*TctB$Gu+g2KcAlrYO#tsrOpVyJv&}5Kt#JrE_^dh`@E#X=hQS~tzo}NSIH#_p}!)hAA^XhDW z)z_0X-{Ds{AB$fc8Y2BLGi->;9j5vG+|6hEy0!ZkwKln#n6Tqs$q7RWG-2EA-#SA2 z&q@$#bf9GXx&qeoucOMih?j7o%oP?d8(e0bR?%_)Z9zwZ_$`+V&=K~NfsXM83B&hx zOvNl48y|`kgM>^tRt5|-RhO{x1n@kYTsryYoj928nJq9<%K|Le1d$|`)L1O}oa8v% z`wUZ>N0Sz15N>0x7Z_@p#p@T9B1GiXqTF315)lq9j9@8EvRZL*A$QxzZ;{Zf8Czzm z@q7K+CrY>GS$d0WNwMoJ8^iu7wy82Zj(ju2$JU#uf3%%6Dg|2^fW_8Z^N4(w8j#Yn z{k>30&u}L5eFD$M4XBl)^leeAgoQdBgY|7UwUQ0cVdGnI3`AB`HK$KR7NQuaL=h`=v`bQT zbj{zX$O)RePvzWxT8a&FPczH1y-&tjmJYTyra>*BbV{>j2_1P4MWVNF*#wEL5<1I3 zVkRUuzQk0!1)|a}i3w=7SKol3U?>e)m6Bc}kqez^L5?0ueY^FQbnb?mp*s1H0zLj9 zw26FE$kl|(XqDIIIx@I@Ypkt_kdV4LfqRP{eWKhtDHP%dN(YqC&A}JMN`q~cLi3W= z0*P)fI+~}cykI4Yn{cY%PD;lTzu@!bFMK)U*1TkQg{rVahY zCurY+{vtwX<-{#&!2`BMP;A4T@?GgIWF1abTiT#kbDWI>#W8UIO;=Q$eX2PfcYzf$ zWr~JkD=>0P3JSRM2KC29%1=cCUPST<%awNSA_nS6IBM~~a``a2-gaE2Pt)kLT1?F< zUFl+hD%fvX1+ishTU9}}OC$EVx33Y`eOH*pIZZoIi*=BM=~ROCmDcpx7MdXm&d-s$ z)95sgEosA;DO1%PrwpI%E5kp8Snh0$V;Z+kZF3p{-dRZrQK*BoV5Z@=%=Q!HOijY+ zz~MaY<84JhTZsDGLcjVH^lMcEX2&Fr>LIgaB4U=Q8nCE7RHRv{+|Ud$E4EVsmI|?_ z^*8g(#&OnuVm8FwF4>xN{o3|$1by{ zK(Dr2pOrgChC;iiZ@YRq1shWh0IZcY^9sf5c1R<@KD%{7l|eAtp~Vi|xe`ySgaIj` zeS|^#yH)*G!UpGGJVprNJW0}r)%i$tr`_-dK}>q*lATEm{tSP6cB!_H{Z+eQP_~>PI!9?LFi(5<1%2 z%T~rbDDWMSmqd?TlA(Lua6_U&bhH$H zx4DBRNwHGx@w2FWX};uq-bjep%n18o0iMN4I5*5EW3f}t)JT6U67f|BGU2Xs=brKp z2u1?g^Sps(VP2Ws2F!~U)hWO64Xf~riR>4A@H~*q$*pA6D>~Ij_cQm8F0o&H*3q?a zcEYuw-v`p-qjKHeFsdZFA8V;z>Q0F$2w~Qb_T5dwR~jguS)3c$qo=nE&Qwb# zMShxXo`}{dl?22TH>brS4^NF7qznC}or9T0G;&I|*XMgDdk4=aJYRp%r19cW&SuoNs+rTYb@#xk7x9BAt z<}#d&j-aw=J z29$IE;QMKrG$f=j~k&f+V=i)#*N*EW!&gqqcT|Xqx=qMYmZ%9Lk9e2SQ13HX;E0Bss{P_X^O26 z>jecNdQfRSvR9Xg6?Vd@ykrHG-{Z!9rQ3Yrla2PtDX=3l1L6c9c){;968jUjoBW1hvBp*~`f+6jl44>^|QhB?2WjNeGoqx`qpWFFzrPlpl}UeLT? z40Pt5Nog?eXU_LO`7qzX{DLvSoVUz8Bo!;~hCsN)PQi>&xX=zx!Y=Qn8qlUmT8nni zM6++w*UC!;yf+~orr=P^`CK}tsdbJhkgOi~1* z^n@uq(uopk$LN9idNhWsdg>lR0R})dkEe_%p$p|+$6d|de6onxW8cBD2J^?y#2V*K zaHl*qIkns zn_>6X<#rK^sO75jYGikxhN=>}wYGs< zDt%kWzXjj#;S2m1$w%!FH#(rMTs&Sy3v;P)B4&|*SP%4KSHbitaRlLhM+T<_4%5O9oAu#&t$k>; zcC%7GH90=d3qQWr)s|LlGCZvoir$ZEbLgZCCY472J!Yt<`3n|mAq-L2aP3676YI_k zp4&dUbwF@SXsefq{8us#YpZ%L28RE;_cQbAI2T)Xjza8}cAsVlQI-SpW8Kfjdg802T1sWB9y9TQ&HL+vp`>aC)dw>q`F)ql4CVQtt}am!n~wY=4% z<*mNvPAok+1|kXh99?1-cdE(wTfd#Z^&RHEPEGo&Aj4}4Selnc;bJbtp!#7Qi`2!W zBgRt&3}o@BdVTN+-pswsnvM}%Xtek%NNDs6ECKfiEASV)0=l6UILz#r@X3mwggs%%S=o!W89$nOQ&cA{v7LawX}8egTL!1^}tG0MQKx zkjue0XE}!T$#M*n42tFA-z?)P5o>sE{z4f&F6o4pLbDCjGL_E4hSfLlvS%p>!YgJ| z9=^MJ<#OuEx*M*eOIqzHh=8&j*Id@I+{wOWd^*tVDY}qnmLzJd4f1i)Q7Eu-ISBxG z-pxFi$G%=Rs@Cn~&y;{)70H=Dk(~vu$!!3DWHZgi&a}S3vAG74PqsNzM{zEK<|KDT>OU@mJ5>YbJsvW=y%4Yp>kxpoZv&h1Ty zT33c!(PtOQz3CX&-!W?mD4UH~7_8R#)5HP*`d z4Ov}v_a%3?)|N@v7Bu=u!1o}k&ss5i1l4-WGf}Z7Kt;>^s-emIgPI3O zp`4DMN)v~OJ14@|90u0~zmpiOFO#v&JBXn>)PDPxe@D7#Cx-1%7pWMOu=}{}#q;(K zFeq~sEQXZ0RX+&1|Cf!?W0XhdbL$C^V_sM4kp+aA+f+_u^^nuR8C~Ml%K12A1Lt?K_&vXm=w*B+IM1+krc-jwGL6qmEZd@}%MkTyPhh8rctVI$( zCCS6c^;#9497>93qyNpTs#e{tQm7jaU8nO4u?&b1wg6x&1Kn{#oX{S;M3og&Ct?A+ zF_%FLuEB)t-MK}qf$jzqvv;p>c{g;|O)*A(OXkPo)k4y>PJdCQ=mnF?gJrT)Ep46V zZ*8NOO2@pOx;oA>M5mz{1aEBdv}qLaQ<;W&IA6+*>_N-4wB^gVzpMFN(d5&nCL!+_ zqaBU_y|)q-I&IPMJNKz3@|-gFESOZUcErTs9MCV#dwOMZ7EHEi#Gx(eEm)teL&$y>V4qv*e{?u8K23dhXx4!dRA#wqxyFgqY)6=BMa)+W|U$_xV?LK^Ai<0Gb_2o;hPI5+a z^??)Y@L;elwk0yPV`;5zP?^gYXUkdfDN=~(Rp9%}xXt|bL#&R(WH&m)V<_g=bFkGd zm)*_DR&P?_HhTMzP^mDow@5Bo(e`1BlNGISFZ6)boO+2U-fR~t%i+DHCT*_ii^{IH zkak*SMIn8Lm$!ly*!^*?VMp{@W^GG%XX}H0W))m=vTykC~g4nygW|*h714h)oZ2B!Vkn zt{K9S2ISkE&*4f}>Lrlr;_$)j`OfANqtnR*+nzUK=7;=W8rh2^b^ah4mEeuEd6q!T zHY9qZp?ve#N%$k-5X7dhgM@(B&tN`N@WqlmmsdNmfblxG`$%>j-M@u4XuFqCUue34 zD+|fzw{6HsgNKtbOfKcY@XIXKN-Cmu8%t-f9n@^KgNih4r-MVu-e!H)N%$7GmV3d^ z!h|XA%jPVi9z3(I1NZ&OcjC#5)B@~gp2-I+n@2-F0VrRW5_^P~CP&HG=Bf4dShmf7 zjgvzMG2&k4Z{w@I;yJuiOtQlSc@O>Ar8E8KpYyygGUaW6nugY>A+jQ3K=9i{Cd?DdA&FuP5I%3^Mc!l)KZk~kFXofJy{ zYIef8MumIxaIvv0YeFFb}tmhIe(*jI_Q? z$Z-lC;7E<~7r3tN1y;tgg;n)-ePt{HA=)Q8Un0>tR6EWfbBW-!Bv+DkmRm)uS)Jj> zq%P5kno~WJ>s5mkb_o>4E?NWbfUFm;gTl{^NWS*=db!uJJUCl)z*g`u#j2U4Gvuep zHX*3e@Rtu#;P5X*&qVb1;SqM>e9h3Oe+#9nzc|J2{!D4+gY3u;voo+9XBiF$&08w1 z+vt;@B{7j7;S_}epP1lK*WJ{O@%So!l?lva1OAwsW_e?GaMYsTKxM(rWfJ ze}fSK!v=dFI`v`%sn)oQ}-3HS|2IRfYl~D4pT|k*mpXTH(HG3Gq-9>ST(}z zp0wb2L{%q`s&R=HDh;*yrjFgJE#gyi?=;l(_4aG$?rJdBx6)n2C z*>&?3r8DW~bR(plOWyh2aeVXFXUntsMJuzprA0TlyKX+&sogAX(aj^S8^Sb7&2)}g z#B*oLf!R_sB(pDlYIeH4nL*8tl|6BHNql*xH^C?(Wi){lw*(o$Cvli~lGF_P1vMjm zTXI3`eQH+gaCJ3qR5pk_lq;TaV^mp)e~>Y%bb=itXWO}fWGY9lQW9^iARSuHWc+0A zjN=c({xd7b`EIGlrubMd!^=`?%F0P4O1O8Q-kb|r!$p>#_mgBlhO8^s!L5B|Bud_s z@}0@4@xKhl_Gl22by*I_i!GdYjz#}zh&tz;=~a#Nm`^T!d?BgKIHk_?=ZMk}7vgT~ zWU!l@^jwr#+nzhT9y9Br?1mdKLfSybr00%(_+-?%K%eRNNe*)L`CLYypQByKU*mKV z)uEu^yK}6l+VfEe-gT2QeRqycS(8@)a^SmjYygZ+0WhWoz>w`d>AQ2R`NR+*U_QtK zuIEic93{KCKhjsH2s+&B@x^SgS- zhsiwGT5Wo3-HEMqt`Ki_&;eMud|zt$PG|W7%!u@J*q+={({Z2-mam`ZX{-I{dVFXf zEXZ~##QOznx{4F#0!5e{hy8lP=tg(${wp3IqYqZv*o=53cswUN-WdHACxkj@=a)*5 z>ZhCWY|#8yY}t>3@TXtXMAi*7u^@29yu70_?BS`el%T&hr{k0TE7PATrZi1s`qc!| zeucN3?^6@~H70^Hd>`&hI+Aftv56(z?rYApw~sTobfikOpoCWTYlRJ5kIrx?^!0F+ zHu+Pjt=M%|F4(1;^TqS zRlIq20In5^HaiYqS(Ove7%48$XqRbaRjrds)cl?|@ey%CdLx%`Ui;C3=852=CH_ZG zR5d#9b$w*RRId&`x+(Zb<(O z$qBdKOC7Xr=TUIW%Z=>W!*)5-?{ahSfME%h08(^{M4mLDOj`7j)M%FSt!%=yg3)!dV}NgX=A*J%#MmC01-QCXrU z?|Z+;%J;Vo(!vypVdE>O)({UF* zj&`Pv^|_$n-LDP+tfYfZxsf9f_3p&6o8z0uq>SBR9pl&N@^(eaaW46|e%v;tNe<&r zMM!f7<6KN!5<^!OU%H7wN|}mVEoM)Hx;4XFo8!D9>(lb})`pB@MQf_j(Q5)&65C02`WH9}= zdI{*gNUEmtS;ZwhBnmJWG5)0!eOK}(LZJM+j!z>uhTmTY_>didM~`?vNtD);)WcX% zulgAyy{?KbA%$g^qxm~A0aeJ6OXfISM1RKL@d=!`>2d5J*Y+X7z{r>X-)LNB8iDV* z)N3ZDM6Lg|P=1wprfy_ORIwgp^@o478U%vymKp@oBwmsh1R|+!T~?E~_x<*$?%bSm zn#>IS@b-SpK0PnvDR>!1f3VDJiT=njod~i>4FN%m{%wsE>H%nZ$0tX??Rv9Maqy5C z91rFwase~)ZyTb&k;(R6LwwlzUf09zgs+=g6Fna8xEWpXHylm8s*~{L&PXb=8U*;i z$wd3;XqN69$~OR2n$rMm9k{+=r7H-yPKOQYyII-a0$kRL5?4R)RMtS+0>?a>~lJO|) z@c@Q)oIQ!zFL8_)BhEyq8MZP5YD3_8KG~zijxHu#);jyItF%EvUn|XmSx%mzLts*C z$6&#FPccXuzoL*1AwXke!Ukb%)PZ>)&dYstu7zic<-pv1|IaXNiFL@lRYy}r3dQHd z@&1Ug84|V{S*AAL*!!!XUbkN_tHX6cWx+l2&vlHSjBawl9&5xsE z-LB)r`cwtIS0)@24FN4#gG>?RX`suEGE)zv*+Nr0u2JALpX4$b#Y%7&q-=0tG#}@J zKJIoKQ|u-;+gBsanL~LvTPJUY3C9=1aeSoXm2wU>#qSoXL#fN`E zOcG0Q9`(z#Ce4$>HEE>BEa+(^aW)E5h2Y_{pm$`)u<~WN*Ldz|0jn|-N*9}~ z=8~&s*K_q)t>^#CSg+5}u%Qh%O!bMwSz0UCxg`!ujplfE#~SVyLIzm1SCAwa*3All zeY@iKHZW4QeO7Irp$_NP#mU8cnZ>h(8IQDsU9cpXTWTqx~6 zq=#DoRDbiRG`%xjRms~Zi{LTA(oc- zKOBV9hCAYEG#U5lU#ZrsPwVhX(5G*xrb_bDXFNZ|2~roz1ee9ogW`iTDEfWewG^Gq zBsBCAi*yLW__eGIcys#%*`&=UETv~wV^P)8%^lCu-7>*0-HS1&zODCsjUjc>*2aLy zB5XffSu_b?qzsWD@uZEg>0#~?CFXZXmnETqDr_BHH}jNpAsmRane@LZ?+p%x&%fIi5Y}UrfGR@0zPLXrW7gQxglNJB}bYh95{e@oSS2B(@m=`Aw;#ZUZH-S+M zFO2y*AbS8$+zuN3LeJX&d6TDwbez>}(H~j{!mr6{G`AB@+*iUSJ=`@_CFUSpFJT$+ z%=1&Mw!Mn-CU-@e;Ag{7RIZ6MgIX`i@$O*-M038v-7D-Yr4Kn*BaB?QJT4M5>^l{)An~3-*!W(^q|>IN*67AG^m})s+W)O+YKfow!O&Ga5rd3hNl|u*y_6;P4C+=E z&Or6<0Xn`qQKbi%`7DpYd@`|IwE03633z*=j#*}V#V{Lo~+i89gYss_rKKZ)|f ztV!&~-$|X?pLL6TecNTeE zuSF{B!)4^CY%hyn#Vn&;E%w%3Qyblq*7{3IQ3roXRcV~bw09Yit^T!)?ODbjW=HKZ zKKh+5<2!6I&m3c|jp>{50PV%&>{bldaDeU|pWKM=v30c%mC~2Zg1G>$4#TAOthUFk zlOY9ckD&T-GpNH6#d*B6=Q7DiY#5>Z2TI>lqtszwysl^IS_rr~LfKVAZp3o%D*SMYZrh+TKCTd;8#g(MS0X*vk1>7!P>kP zmAnx!7N6jxFqXQV##rbW);mYU@_mS9K)XPTkkibyoMvHFE1c%JY+>^CRTjNN=s#`m z-lDrcr@1hybemxK+i)7oG?a8AU>Xl+K`K{I8btNvaxZ_7>L=qNbg?+ln~ob@q3foA z8(k%8{)*h)NQlFT4~x}%MNP5k-j6AFjuTMj^yv^*`!+6EVo)Brx;P7LAa94x8$POP zvP|f?i1N!t_Cjp)*-&E(HpE7|2LSB(EX;J0!oqX4)#{`+{FU&`y)=kA2JvGJBHkG{ zyn45pHfOcQWm4C=UX;pwNb&3MjNOE5SptnnCsb)`8Ir2BwX8|Q6%nd`Eo>kZe#0iV zWb%s+e@ChCZZQt?O;x1j`hJaEnQyA6rtj6y?A^0<*Wau4hFo{rs}ECMt-ahxyh)PP zy|_bW!F{Mg%q}@sOo+P57HuMRR8u~K><;ePy>gOWIa$Yn%XRsHy?hum!Vj+U^1s4T zi(HJIa-6*??TxN6F5v!lHIVsZgQ?oF?v zT%3H6QAT-pHp)6QLdzg=(`{FmWLu>{cp-oJ02YN6r$>C8cK}(GtO5Og@=B`E^y|dq z>&^EF_aBT#{l=tn7*Dx(fbuQbH}9sZZMoi!@v^Iu9lZQHJhXePH09-c+J5=97BAPg z{qm+s)wEn}Tebiq2Fmy8O+&J>jp|3R&gJbn1~ zZtQ^q;Re6s1tg{7%}4M%NqrMi{yttu-F2oj*y|kbrj^!>sEMadD16$ zu-A34*M~!|86Dkccs{}THtC>+WyJhYAT5ls|3m>gd-7V` z{Ey4Wy42J&ks`BpauX{^X4Eb$uDNh{9k!NzmH!PU(=`^~gC?wn{J+#fH(JOur^)C0 zT*5nmTg+U_DZyX@C|-r%s-c(FkhiB#R##!3C^=^za$C(iM%7d$P8!4 z@B*-1%kj+s*}V2=tk>r?bM8E5kc93h^I@~Mj=}cyv@#yt!d-g*G+!7{ie2go|L>LD z27;wZ@WYW-K4YCbra|w1m@hk#-kq<%re;j7Zhx6_+kI-^XmN>;NOhFVS20suyv-@J z8kIr|vt5iMlOO@$zvld}b^c$A+bN;e&#|Rre{ls9IJEP>Dn_< zdxlnIZkbeqCVU48UH6CkGw5B-i&+V_TpblgYBZC(J2s;hB}_x292l5oD$M;*0vR;u z_Wx(^Jm9M;vd4duoBK$kgd(7z0eg$R>>6=hb$4ake^=KH`rB1pjbax92vRiI3&yhQ zB1Ey50E%sm;;ubdu*bSsh>BgYWBY&4+q3~ye!XM9*xDxkv&WWWy9P2?i>DmFv(E&)< z(AgYA2cdFe%USjezc!+1#aSTlGf1(A`;>XoTP3N{rxPkSCx!;W*|SVUi*BQ*FDMk& z@G}U976QoK9JJ8Kd8>R}IKziQDg?7-6GsaVcxxdwe}dFom~gK!e}q5taVhVPc=1@XM~~XX??i8<-bZQ9lp1F%FHq^ zpH0FTYPucR{N5Px-G#5czRKJ%5%649B|1RLV4U zp99Cn8TmfK=zZ@<82SKYh9jLx|I`f;K zWItx1eqvChfXC@7Op+g`&`66_*XF#UN^j6O8o$3BRlizQDpa_T%`+2}d&a~yWP+k^ zUQ$%%?Qkmtwx6h($#_L4Bz(M9YAc(86F**0oyK?-qOraE0{5NZ6veYz39zt!KUhdthW&0-ph)S>~9&5rxsxrbAs1z62rgB9Hf0x#Hnsg z6~@tIzM|8}{Pei%D?P{rL@;l~-E^bT^SodNWAf5TO%%#F9$e25L+zl+9cLE2A=K$` z2Kq+$``5^+_0UppAjfi*#}FS!=~MG_w$Z=6*XMz+GZ; zGEJ>bt==yxWzD&m<%OnSRa63ogw(y+s(&}3QZ7@TTFjdE5>FgVFb6$n63v0VJR)>fahNP^5FJl2clFQ39D+3X1)`BA8fTtBm{-ht_!-^nK!4T0Qk2{LScY2-u_G4H0|vTh zR^_uIC)c{V!52FW8@kmxE6aEDyyX_nTf-98{S%Ga;HE zT^o%yd8@q*cS%Q@s$G!9PcT>Gzh?><>JDq5_J@GcEVzwe&GO%tVp$N{%l#_a@~Yy% zLL%BZvLb#f3oT0X99U1(O+vHkmuS=brn|AeqpQPZ}p2mqgC;* zn&MyZ%l}WB%736$`3-*gpK(#MCGkJ=%MTE^E=>r$(h7mseFR=9y-Kvxt`C3mG&bbuy}jM1do!JosyAolM6kMzNB3BI z2#+Ee4ld}SMn!M6*au6+>b&&OGx{;le|>DDtTxsCDsl&l$TS#hW{uP#_31>m88A}V zkP*Z8?1<46l_9pecVGZL+Q;kqnGtrL(%6!yS7jeBM;066Y1M3qr9L+t5b43Xy-NW(R5*>~)`rA6=EII*&GBd^8RUBQ# z6QsLhvZkwM5wSA`4@}W?-xNi+0x|GT?jNH!9yfqM54{_E6zO*18=|0% zw+7qkj)if*`)CpBxLjE?0dbtiQb}Cs{(EN3vztQp;5E%^*{Q?Zi-KML`LZA86chDP z1Rd^Jc@3R|qI`E_*m`!zz-KdxbY~-pHk)L__aRE-i0lAXk)Otf7S?!e-k>SV+Z_`QUmXj{ z<@#h3s`5tK1J7T`%Djlj4yfISuIzAn%e_x(zdo{H;^xd`%ecF=_5-;!+)yZ%YlT&H z*d{=K1DearZRTw~T3^qa}K?%ZMJa5fdD-@vsxk$0?g%&`bTH!gWViDQB%)J;Pa#mSSLXUU+!iM>xlU~2e#py>rd{KjPzYb_J~)*kKjh*{9N}d<9z%Gki&>Vd`_sdC z{8d)v<$~^&M>hh9E#e&^{zj*E}e-KQOIui z|Nib1K@-1mEThwJU?gKk`j(!v}~3kwO{upxS5*Gk9XG>-+vT+@>CkIP+H&9h+9$4H5*oYCeq z<(UY_LxgZWNQNAT0{yEec)2e#_n=o|v^KwT;Tqt9mX~3RF zk$W&Ki2wN8=JdzEG~Y#zK8e>)q`PwqnN^uVxTzTXHlDZXFN}2ToP^Kgd7J*9i9I(i zmZ>{>N1C>azL3`sgSXyqT|`|xTgQy8&(_hpflr?<-p* zsl3@A@i2p5`VvY-Upf;};3_(tSz|BD!+L=P zFXL+Ip58CqIhfUu-*4w2^SK1!b08P-L(iB}>=@Em7W6&g z2;|`LI~bYfj=HC8Nt-5`NB_>1-S=l$GezKfK1Ol}Mantx-SiKpXFkKBbKKqj?->?B z?6YyP^xIi-a^Ved&&;J$zD|!R7XIO#+|hK8jp?$hrd9j+VbS0PKyU8efwWRO&!b$1 zl0J7XIP~61hP50Q&zCjzH9kp8H@-rRne3t6_$&G}*X|14rEcyHEG_@xo>^b_KCX)u z>aXc~mY6-oZ==$xiD-Nvn^q5-##GG`)%u_O0Ze z?ns2=3$ba#f;QNFeKM2uv0_Zti1k0@+MqC4x4~dxmfn46s=UFz$q7hwXR=JdftFy? zj~vm5-zhKV;ddH|SMQ?_zhjn*Ln_ZKFf~cz9^?Y@f8Z9!8abRN7G$<}m#~zLg5BM| z!u^;-s{m)8DNKyBrfI8QOePgn&>WMlTfr$@@D@F9)62PS@KQasM%1&3Og*4RawR$4 zO-Sy)a?i!Lu_*FANPCrXLI^RD5n;x9ECwZRnXcWE3ysbGFxrh=sV!8XLoNLd6(Kl3ksjqB%l1^UV zi(7M8^8yh%%rfpG%7xFxUX29s+qc-c*lQ3*&Ar5<*ZRyd_`@u_tGsG__Ce7K7eBu+sAW)qz;rp(fo&B5l)vd{ zGB#K*7|(?DByoGlx$E3ql;^3yV6@JIA>o!(#&mQBlf9$wDktpg<{p$IdI55t%=v`h zdHkQ_$$e}iZ_vRDz0P$M`igtYOcUTf>GApXeO<&<}ALVh$6czc`r=bq_2B6 zPb$bM#(DnhMb!k-a#!baF5+;?hSK{WexZ%ecl0#Qq;j8~SZ$hd0k8CBz7^@%*XIUG zDAA66ld+*SjRw;gu^j2r+az|4e@-4*Zrg_!5(3c5pE@$tXG|c-mT3= zRhUHEvYJYwTS!DXFGVW-`u5fOE*>UFhT76sRfm*5gg89 zb??E|EX(%~Z}9rn@WbePJTSb>?_&peMbXr!i|LWC;T#xJm*vnmz2f{?LKydP=CT8@ zoB1K;iUyA|eDR0aQaFGmY3|gzy}e=kbFhj{buzxiaz9vX{}j7>)sS_SBNv!^yI0Pv zL>U+0OjC>lA}*g<>dV)jk|J*g9T=()@)UE zMqJvF<~(1(Vnj~C`i;3iQ@~!gFxR8jY4)}A`^4<*D!kEd1GfG5N0|EKaje-(u&oem z)a2!Zf$mEa=&SR23S|faF&PgR&RC@!MQ3Oy`m+2$J8^WNop>G85_QOU?L_b$FOs(T zOc9urTbUzLXkTwqb`#I>*vL9N1wplOnvJbvBJOX?p$ud25vSVgKba8<^V!#FVRFDQbI!O%D9@cHDnjr`lhA-h<8C-@Swvx(nO`nB}g;0tP6pa2hDQB&Kk`nJ?(w zBsjbOLm9kfyv!)a`9)sYyQa-VaDFq{PY5;@Aj9)Bt&r*M9!9_1oolbmTrE^zfy^*? z-(dpx<4i|mChHlhu!_CE5>m71O1NBBLUeom3Z5Zikgpl%bVM0R-Kc_3TSk*0&U4R= ze$@_VA~C$vCvavw-YI%!^=w*RFubC?#&I}H%3Nj8n#5|RA0jI1e{kO@@?YOJ!ZNYk zD3Moc^5vTR%D@`yD@828Vleh_VQ0TZQf2JZ1fp|;2%>X|br&8JoMr8!D1HTs#(0g# z>%p&J{AnLX!!_vU_`iJ(+{fhL#2A2+Q=q*t-UV|P%ryl%pls6ao?tY{A1;Q| zS~&CZcX9*~t`h2ey9@L)-Q zeOiUDdoH_~V)yYAYP|tlo7LxP4oAg|<)u^VNLuWESy1h5;cisU7L|NwOq8xmHDAl2 z+s(&vBVSl7l2?61p2evs@&4_CeZ7si@8JJlfzoK(zXdT#?>}3(0+SFjL}G3b}G$ zQ=j8E^%vUITvH5s;VzQ0ZR(Cl6=gmYtE zXo5PKZ=f=11D{1o*gnkN`^u2K5h_L8g4_cbr3^{og&q?6-aUcd#FzP?Taq*?lx24! z@>$*$fbWQg0*}m}N#=9OEDg)&Ip(uvsBhkToX!wW=uC23335RZhKayq935~m#yZ!E zpcLJk^HH;`!gw*dMgO-ODj#ek;xoC&ET{C`$ZDm0FitoF7)m*SM%>v=br0f>z-ft* z?h;lPsnw@BOv`dKP47ra`9QKmog|bGMpGa&$E%L7eDFn4a>@sO&tV=E(|m#Q0W5p; zD4Cd6$_J*P*W_We`7;A{;Iuy7NBO`^x@g-#kpAwTtbv%omv#SSF42sW{hXNk_XFe- z;UC-&CEhX~s!VgC`022=31JSd%Xl9dLxnW$) z>VBQDz%Me>FhAVIEN51&vr*C>$9=ocr_^};=8J$uSSzNP*|~D}P*A^3 zkpOG};~;GM?K9{cykbYcMz*ppM-d{vxlIH_%cR)F2#EMF@7ZlGvE;eUPx-QZUyDHK zW#X?T445wor0Zs|iF-x9uE9z#6%aKQ!QG%lMH^!)x((@MOG~;pc}2@F; z#~70CZNY5L42pl+hwS!_*0x2S4al?5&-1+Gxe0t&jJaC~g}0Uy{dMGF&e86}y(M3P zd)s*5y29hw4mH2wJ|+I~-v00UApYSXz8*!d$nNl#)4AM%$zK37vpIo@^5`^wmWs7+ zDGby>udDJW`o3oZ7 zPnV&0bs6GKY|$w495tQ<}B5&I6ztk20YMufiNM8zyi@8*tXbB^8+CpW?b%@OS) zj>O#{g6875D2UONNOLiiv7hV(F37ExMt6S~=qkw*$8x*N*Hz+n7s&?j0vz1QY}D}( zmzJ6je~^Bh3+2w|Bx676?4LOpVt9#j&`SXL226cnWR+ycigfd(vHci>cZ%-WYVN@~ z?rBI5VZ~?31=X*(IcSayS7O6`YmPzRoFXbB#qPfTiu>u*SayXDrAv0Bs!bcuX=!c)r5UZiX=#X@1hxb1VBqKWn<==C0NF1LPy4#R%z(rTDxBfI-j+31Y%%D~`J+lP+=abLoK zV(iim2^e&L$3!LKPu_vI${I}Leca1$yr(9M&MGV18v|vr7~(!<*z+eR!(WRIrMsMP zxvzWo1m5wIAoc)MYESQlhiR81V`;Lp2Y}&squT`h-405qhB@$6Ke+Ek?TF4amQh>Vdo*y*xF!0Sq@o1X+O8c72f@ z0P<80&?m9%m~eW-KY59e6Q2(Y#P&gBpo!nIJl*Z1~v=CzXREmzJv|i+1q_9M_Cv3>XoY)W^riz>(w5`pEC1*`e$w z1x?;&$HtI>Z5CxQ$v_dl8cfJQas9nIsBO#MAo|;@m^d@4ZK-85B*ofx-EQg@p?iUz)AG9qd2ar1;;>^b=1GcF5 zi1c)$45qpbg=OBbg?WP!G2AO3yXv$f((BL8NX^kgJ<*YXi zx2un!Pb|T7^w6#0^l2O9f@oRDe&rsqp`C{7wbEk>S)L1nEQmQ^ zP$1J@ghBHokO)8BD>x=cMl8gT5MQQUhmK9Kqhcm8n(Z+)#Plw~jX-yL0HfHE?L#hc1pb-4Wie-D>W+ z5a9lRyiR7bvoOZ*aWR*-QPET|#v~s;L6Wj$-BIW-dW%oU-9ttJib0UU{Sl1~z2rK{;?Elmp*G(fUZ%yCrh-{jlg+svaVk3YC$9 z^=s`4ljDA_G4Y$>YBaU{R}*KSD35kG^BN5#Vf|efjfnHPoMVRYNGwpeO}(iB3W~(% z@}f{>Zm#TFl#uR5T)rv12uG*o3?4wWd`tKTXf5!~J7X3ZAi_8)ae2n&(^I@di!A$my2YF{HOL*xz;PK>1RX^O;>f zU{UUqyYT?O@*}Y4s5KM~H&LfvAg1Z#mxvKEA%Cul{n6UViRjQmrpqg2XHzb_AW=-{ z6QO3aFsH+c^PLl1%W11DW@d0ZTQteP;7vN1fh$FdC08twI-P@{L+01FQT`KE={ZP1 zL_K0PN3bB~ekUdrf8Y>2I5B9mK!!Gz^9u}g#f2$YH4Skbf)j7h$=;ALfZg7`iZwzw zp0}_#wj&zXx40FAk96OwqPWj_WDJtjh{F3Ph#IwMe?=WE)wzt_f*B4Yyp5w&z8K4K z+wxp1XKC)c7;rGF@=a`KYGlBd#?@x`8{MtG4X2ejrk+aq^T>5(e6Z@AoQ?R9D2L z6xF)-dR0U!DuP?}JaNb0y+QsQzR z#({S3RVSdXc7ZqYd=vU|FRAe$_qr2!v~jjK^0(R0EFHROf2eKlsdJlqYIh4o;ejzb z61l#N+2?08$dt_vf*~!MwyaT4atO`*cpEn;jKcT`Z~f?Kt!EA_W9E@L@FB|_Siwb? z>e$JyF2Q7XmfU>V0kO{Z!F=}!GBlm<>Vx_2l&-fA1OSpYI^{`XnLr*+ za>~0VnDSmRQ{E2lHB67&V;2n=92)^SnD^>W@aH`rZZPk$q}|VXMMeu-y1)h~|C~=76M9T+oX3N3b zr_#vhG2#rid|$@*AP}!GUz8JA|71(exge#mO&|#@;OD#rCRerQ1=mbwPbE$+sExq* zGw*IP#Lk*g!$&1P8Z3N`_I9fDb`&n`uO{oMxg?;Ij8A^Ud_GT)?BhP@ zFxcMF1IJ)~$*)f|z0@Uak=jHp5*FB2iv&`YC}|SUFtNxa*+$9DFi@o0P$cmrGmp;A zCGTNKqDyC?gD@S%O2gq_fvl~RjYnBC=PVQ5nNy&zZ*@k@E{pXU>FJEyYUDSv$9Yo} zRz(ssLQbWwVJFmw&~rHOWIyG8#vzc*!OLClTePC zQHG2CI=*Kc*;fHa>!=61A53ZlAbJ{7jC7O{tmHg@HXOUVPw|T3&AFBMb%N8ax#&l* z;2D~KY!vnj+@@mxbak$1^e@M#2>7u<9vzP#zlWj1Lmz%c{@p5XV?QD9PY##z`0>>A z1|NB1pP$~KbmJ+-d~0 zr}(nb5iDxD#4V-wt^Kwc%&$3qtNeqyteRG-z0Ow@_=0<(65Qy{cF_)8B`;f;>GjE6 zWE$?994e1=xw2)xE4;N38M^bhxwxMDhBR2BI8*gehUXZH5gIw`DdSaT1lz$(2KrHk zV7A#vujgJhoi6joV7{L%Que^X79&-G$$Sne zm=P2w7BR?et>~5?83W@EO9j$djZRA<=>n4|V$Ftkypkn*GtAEOmncLd48|y^Kze|z zGBx3WnS(F%*!cQvhY^x651cZS%UOnj6eS26*1cddEDuX~6>Qaoat-a2nH30BxHW<- z$8bX*F}l~~_6LixgBAuY9ZWZYm}3JOj300SEwjy)%oZB}@HJFHMXG#kRo;e-{lolU zLn};ViP$9`g4Ja|@~VlxkoqyC=9N@bVgdQEG0Mj-J`g^D+vC$$!LyrYgOBLhocV7V zzzcGqVa;?N;f3jv3F@jRgpPAWOpIw{MKGV78IK5LX42L&2gb|@#F6rTHTM%0!hMI-8GY?S_chQF zdDvCCff3)>@o)}L2W%RVd&Z72Q5N(QU)D#my+R{&8Z#&*mjLh(KQQE}{VYDhL z5^LV&bP;Cf3uYg;#!P3^IS_2PoZgk*?G3-l+u$*%Z>~T@+`T#E5;4~v_JYRXBrZ5%ge5zzkFRfZ#zcxm3yHZ`#>!e^ql`UA{$WO9|7(^3nS6d0C|UkG zcjrLI(zmqUtYaw<{pJcF%Hq#tc(qjP)oD$#U|;Sk_YKRf^?KXkpyOZM-vX(Xa;0o= zK89ywWX0a#N6c^c9j8XaC85ZW%>ra($oUJ}5+TEm*hI))|3|z&BXaZPL@C;bqk`lM z(DOk%yo9Z71WsfY$4cQsWYKIF`&niw;ZhCb6jKmP@t

KJKhd4C}rpY;8=OPsh0v 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 literal 0 HcmV?d00001 diff --git a/dist/conf.d/smokea.yaml b/dist/conf.d/smokea.yaml new file mode 100644 index 00000000..b5cfb2cc --- /dev/null +++ b/dist/conf.d/smokea.yaml @@ -0,0 +1,2 @@ +instances: + - diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 15bce11a..3a8c3b18 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -51,4 +51,6 @@ url = { workspace = true } async-walkdir = "1.0.0" pyo3 = { version = "0.21.2", features = ["anyhow"] } tracing-test = "0.2.4" +wasmtime = "21.0.1" +wasmtime-wasi = "21.0.1" diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index a4dd4b3e..fb10ad25 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -29,6 +29,7 @@ mod corecheck_scheduler; mod listener; mod python_exposed_modules; mod python_scheduler; +mod wasmcheck_scheduler; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -335,6 +336,7 @@ impl MemoryBounds for ChecksConfiguration { struct CheckDispatcher { python_scheduler: python_scheduler::PythonCheckScheduler, corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler, + wasmcheck_scheduler: wasmcheck_scheduler::WasmCheckScheduler, check_metrics_rx: mpsc::Receiver, check_run_requests: mpsc::Receiver, check_stop_requests: mpsc::Receiver, @@ -351,6 +353,7 @@ impl CheckDispatcher { check_stop_requests, python_scheduler: python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone())?, corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler::new(check_metrics_tx.clone())?, + wasmcheck_scheduler: wasmcheck_scheduler::WasmCheckScheduler::new(check_metrics_tx.clone())?, }) } @@ -365,6 +368,7 @@ impl CheckDispatcher { mut check_stop_requests, mut python_scheduler, mut corecheck_scheduler, + mut wasmcheck_scheduler, check_metrics_rx, } = self; tokio::spawn(async move { @@ -381,6 +385,15 @@ impl CheckDispatcher { error!("Error dispatching check request: {}", e); } } + } else if wasmcheck_scheduler.can_run_check(&check_request) { + match wasmcheck_scheduler.run_check(&check_request) { + Ok(_) => { + debug!("Check request dispatched: {}", check_request); + } + Err(e) => { + error!("Error dispatching check request: {}", e); + } + } } else if python_scheduler.can_run_check(&check_request) { match python_scheduler.run_check(&check_request) { Ok(_) => { diff --git a/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs b/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs new file mode 100644 index 00000000..253bbbbf --- /dev/null +++ b/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs @@ -0,0 +1,142 @@ +use super::*; + +use wasmtime::component::ResourceTable; +use wasmtime::Store; +use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; + +struct MyState { + ctx: WasiCtx, + table: ResourceTable, + check_sender: mpsc::Sender, +} + +impl WasiView for MyState { + fn ctx(&mut self) -> &mut WasiCtx { + &mut self.ctx + } + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } +} + +struct CheckHandle(u32); + +pub struct WasmCheckScheduler { + linker: wasmtime::component::Linker, + engine: wasmtime::Engine, + store: Store, + running: HashMap>, + available_components: HashMap, +} + +fn find_available_wasm_checks() -> Result, GenericError> { + let mut available_checks = vec![]; + let wasm_files = std::fs::read_dir("./dist/checks.d/")?; + for entry in wasm_files { + let entry = entry?; + debug!("Considering file: {:?}", entry); + let path = entry.path(); + if let Some(extension) = path.extension() { + if extension == "wasm" { + available_checks.push(path); + } + } + } + Ok(available_checks) +} + +impl WasmCheckScheduler { + pub fn new(send_check_metrics: mpsc::Sender) -> Result { + let engine = wasmtime::Engine::default(); + + let mut linker = wasmtime::component::Linker::::new(&engine); + wasmtime_wasi::add_to_linker_sync(&mut linker)?; + + let mut wasi_builder = WasiCtxBuilder::new(); + + let mut store = Store::new( + &engine, + MyState { + ctx: wasi_builder.build(), + table: ResourceTable::new(), + check_sender: send_check_metrics, + }, + ); + // TODO - once I have this version working, move to the `wasmtime::component::bindgen` + // https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html + // this will let me implement the imports as a trait. + type Params = (String, f64, Vec); + linker.root().func_wrap("reportmetric", |store, params: Params| { + let (metricname, value, tags) = params; + println!("reportmetric called: {:?}, {:?}, {:?}", metricname, value, tags); + match store.data().check_sender.try_send(CheckMetric { + name: metricname, + metric_type: PyMetricType::Gauge, + value, + tags, + }) { + Ok(_) => {} + Err(e) => { + error!("Error sending check metric: {:?}", e); + } + } + Ok(()) + })?; + + let mut available_components = HashMap::new(); + let components_on_disk = find_available_wasm_checks()?; + for component_path in components_on_disk { + let bytes = std::fs::read(&component_path)?; + // TODO how to get a name for this component? + // Could it be statically available on the component? idk enough about WIT / Components to say + // For now, its the wasm file name + let component_name = component_path.file_stem().unwrap().to_string_lossy().to_string(); + + let component = wasmtime::component::Component::new(&engine, bytes)?; + let instance = linker.instantiate(&mut store, &component)?; + available_components.insert(component_name, instance); + } + info!( + "Available components are: {:?}", + available_components.keys().collect::>() + ); + + Ok(WasmCheckScheduler { + linker, + engine, + store, + available_components, + running: HashMap::new(), + }) + } +} + +impl CheckScheduler for WasmCheckScheduler { + fn can_run_check(&self, check_request: &RunnableCheckRequest) -> bool { + info!( + "Considered whether to run check as wasmcheck: {:?}", + check_request.check_request + ); + self.available_components + .contains_key(&check_request.check_request.name) + } + + // Currently just runs the 'run' function once, no repeated execution, no instance config, etc. + fn run_check(&mut self, _check_request: &RunnableCheckRequest) -> Result<(), GenericError> { + let component = self + .available_components + .get(&_check_request.check_request.name) + .ok_or(generic_error!("Component not found, can't load wasm check"))?; + + let func = component + .get_func(&mut self.store, "run") + .expect("run export not found"); + let mut result = []; + func.call(&mut self.store, &[], &mut result)?; + Ok(()) + } + + fn stop_check(&mut self, _check_request: CheckRequest) { + // todo + } +} diff --git a/lib/saluki-components/wit/check.wit b/lib/saluki-components/wit/check.wit new file mode 100644 index 00000000..b898ddea --- /dev/null +++ b/lib/saluki-components/wit/check.wit @@ -0,0 +1,8 @@ +// wit/host.wit +package datadoghq:check; + +world check { + import reportmetric: func(metricname: string, value: f64, tags: list); + + export run: func(); +} \ No newline at end of file From 01661ab392920f9f66e90e96818c1e6afd6bb1bf Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Thu, 23 May 2024 20:19:10 +0000 Subject: [PATCH 40/47] Partially re-writes the directory config finder to find default checks --- Cargo.lock | 1 + lib/saluki-components/Cargo.toml | 1 + .../src/sources/checks/listener.rs | 158 ++++++++++++++---- 3 files changed, 128 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1377db9d..61578943 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3123,6 +3123,7 @@ dependencies = [ "serde_yaml", "slab", "snafu", + "tempfile", "tokio", "tokio-util", "tower", diff --git a/lib/saluki-components/Cargo.toml b/lib/saluki-components/Cargo.toml index 3a8c3b18..4f26aaae 100644 --- a/lib/saluki-components/Cargo.toml +++ b/lib/saluki-components/Cargo.toml @@ -53,4 +53,5 @@ pyo3 = { version = "0.21.2", features = ["anyhow"] } tracing-test = "0.2.4" wasmtime = "21.0.1" wasmtime-wasi = "21.0.1" +tempfile = "3.10.1" diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index 24efabd0..d325f066 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -1,3 +1,5 @@ +use std::fs::FileType; + use super::*; pub struct DirCheckRequestListener { @@ -78,10 +80,23 @@ impl DirCheckRequestListener { /// Retrieves all check entities from the base path that match the required check formats. pub async fn get_check_entities(&self) -> Vec { let entries = WalkDir::new(&self.base_path).filter(|entry| async move { - if let Some(true) = entry.path().file_name().map(|f| f.to_string_lossy().starts_with('.')) { - return Filtering::IgnoreDir; + let file_type = entry.file_type().await.expect("Error getting file type"); + let mut path = entry.path(); + let mut extension = path.extension().unwrap_or_default(); + // JMX checks offer a `metrics.yaml`/`metrics.yml` + // we don't care about these, ignore them. + if path.to_string_lossy().ends_with("metrics.yaml") || path.to_string_lossy().ends_with("metrics.yml") { + return Filtering::Ignore; + } + + if file_type.is_file() && extension == "default" { + // is default entry + path.set_extension(""); // trim off '.default' + extension = path.extension().unwrap_or_default(); // update extension } - if is_check_entity(&entry).await { + let is_d_dir = file_type.is_dir() && path.to_string_lossy().ends_with('d'); + let is_yaml_file = file_type.is_file() && (extension == "yaml" || extension == "yml"); + if is_d_dir || is_yaml_file { Filtering::Continue } else { Filtering::Ignore @@ -91,13 +106,18 @@ impl DirCheckRequestListener { entries .filter_map(|e| async move { match e { - Ok(entry) => match tokio::fs::read_to_string(entry.path()).await { - Ok(content) => Some(CheckSource::Yaml((entry.path(), content))), - Err(e) => { - eprintln!("Error reading file: {}", e); - None + Ok(entry) => { + if entry.path().is_dir() { + return None; } - }, + match tokio::fs::read_to_string(entry.path()).await { + Ok(content) => Some(CheckSource::Yaml((entry.path(), content))), + Err(e) => { + eprintln!("Error reading file {}: {}", entry.path().display(), e); + None + } + } + } Err(e) => { eprintln!("Error traversing files: {}", e); None @@ -109,32 +129,106 @@ impl DirCheckRequestListener { } } -/// Determines if a directory entry is a valid check entity based on defined patterns. -async fn is_check_entity(entry: &DirEntry) -> bool { - let path = entry.path(); - let file_type = entry.file_type().await.expect("Couldn't get file type"); - if file_type.is_file() { - // Matches `./mycheck.yaml` - return path.extension().unwrap_or_default() == "yaml"; - } - - if file_type.is_dir() { - // Matches `./mycheck.d/conf.yaml` - let conf_path = path.join("conf.yaml"); - return conf_path.exists() - && path - .file_name() - .unwrap_or_default() - .to_str() - .unwrap_or("") - .ends_with("check.d"); - } - false -} - pub struct DirCheckListenerContext { pub shutdown_handle: DynamicShutdownHandle, pub listener: DirCheckRequestListener, pub submit_runnable_check_req: mpsc::Sender, pub submit_stop_check_req: mpsc::Sender, } + +#[cfg(test)] +mod tests { + use super::*; + use tokio::io::AsyncWriteExt; + + macro_rules! setup_test_dir { + ($($file_path:expr => $content:expr),* $(,)?) => {{ + let temp_dir = tempfile::tempdir().expect("get tempdir"); + let temp_path = temp_dir.path(); + + $( + let path = temp_path.join($file_path); + // This will create the parent path if needed + let mut cloned = path.clone(); + if cloned.pop() { + tokio::fs::create_dir_all(&cloned).await.expect("dir create all"); + } + let mut file = tokio::fs::File::create(&path).await.expect("file creation"); + file.write_all($content.as_bytes()).await.expect("file write"); + )* + + let listener = DirCheckRequestListener::from_path(&temp_path).unwrap(); + (listener, temp_path.to_path_buf(), temp_dir) + }}; + } + + #[tokio::test] + async fn test_get_check_entities_with_yaml_files() { + // _temp_dir must stay alive till end of test + let (listener, temp_path, _temp_dir) = setup_test_dir!( + "file1.yaml" => "sample content", + "file2.yml" => "sample content", + "ignored.txt" => "", + "dir.d/file3.yaml" => "sample content", + ); + + let check_sources = listener.get_check_entities().await; + + let expected_paths: Vec = vec![ + CheckSource::Yaml((temp_path.join("file1.yaml"), "sample content".to_string())), + CheckSource::Yaml((temp_path.join("file2.yml"), "sample content".to_string())), + CheckSource::Yaml((temp_path.join("dir.d/file3.yaml"), "sample content".to_string())), + ]; + + assert_eq!(check_sources.len(), expected_paths.len()); + for source in check_sources { + assert!(expected_paths.contains(&source)); + } + } + + #[tokio::test] + async fn test_get_check_entities_2() { + // _temp_dir must stay alive till end of test + let (listener, temp_path, _temp_dir) = setup_test_dir!( + "myched.ck.yaml" => "sample content", + "metrics.yaml" => "sample content", + "metrics.yml" => "sample content", + "file2yml" => "sample content", + "mycheck.d/test.txt" => "sample content", + "dir.d/file3.yml" => "sample content", + ); + + let check_sources = listener.get_check_entities().await; + + let expected_paths: Vec = vec![ + CheckSource::Yaml((temp_path.join("myched.ck.yaml"), "sample content".to_string())), + CheckSource::Yaml((temp_path.join("dir.d/file3.yml"), "sample content".to_string())), + ]; + + assert_eq!(check_sources.len(), expected_paths.len()); + for source in check_sources { + assert!(expected_paths.contains(&source)); + } + } + + #[tokio::test] + async fn test_get_check_entities_default() { + // _temp_dir must stay alive till end of test + let (listener, temp_path, _temp_dir) = setup_test_dir!( + "mycheck.yaml" => "sample content", + "othercheck.yaml.default" => "sample content", + ); + + let check_sources = listener.get_check_entities().await; + + let expected_paths: Vec = vec![ + CheckSource::Yaml((temp_path.join("mycheck.yaml"), "sample content".to_string())), + CheckSource::Yaml((temp_path.join("othercheck.yaml.default"), "sample content".to_string())), + ]; + + assert_eq!(check_sources.len(), expected_paths.len()); + for source in check_sources { + assert!(expected_paths.contains(&source)); + } + } +} From 7626db05532b9ecb43ec6290854a8f461494dbb0 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 24 May 2024 16:29:17 +0000 Subject: [PATCH 41/47] Refactor dir listener to expose module name based on read path --- .../src/sources/checks/corecheck_scheduler.rs | 6 +- .../src/sources/checks/listener.rs | 123 ++++++++++++++---- .../src/sources/checks/mod.rs | 74 +++++++---- .../sources/checks/python_exposed_modules.rs | 2 +- .../src/sources/checks/python_scheduler.rs | 12 +- 5 files changed, 159 insertions(+), 58 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs index 416754df..61e3b3a5 100644 --- a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs @@ -130,7 +130,7 @@ mod tests { async fn test_can_run_with_no_checks() { let (sender, _) = mpsc::channel(10); let scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let source = CheckSource::Yaml(YamlCheck::new("my_check", "instances: [{}]", None)); let check_request = source.to_check_request().unwrap(); let runnable_check_request: RunnableCheckRequest = check_request.into(); @@ -141,7 +141,7 @@ mod tests { async fn test_can_run_with_valid_check() { let (sender, _) = mpsc::channel(10); let scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml((PathBuf::from("/tmp/smoke.yaml"), "instances: [{}]".to_string())); + let source = CheckSource::Yaml(YamlCheck::new("smoke", "instances: [{}]", None)); let check_request = source.to_check_request().unwrap(); let runnable_check_request: RunnableCheckRequest = check_request.into(); @@ -152,7 +152,7 @@ mod tests { async fn test_execution_with_single_instance() { let (sender, mut receiver) = mpsc::channel(10); let mut scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml((PathBuf::from("/tmp/smoke.yaml"), "instances: [{}]".to_string())); + let source = CheckSource::Yaml(YamlCheck::new("smoke", "instances: [{}]", None)); let check_request = source.to_check_request().unwrap(); let runnable_check_request: RunnableCheckRequest = check_request.into(); diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index d325f066..6a5124a4 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -1,5 +1,3 @@ -use std::fs::FileType; - use super::*; pub struct DirCheckRequestListener { @@ -110,8 +108,44 @@ impl DirCheckRequestListener { if entry.path().is_dir() { return None; } + let mut path = entry.path().clone(); + + println!("Processing path: {:?}", path.display()); + let extension = path.extension().unwrap_or_default(); + if path.is_file() && extension == "default" { + // is default entry + path.set_extension(""); // trim off '.default' + } + let check_name: String = match path.parent() { + Some(parent) => { + let parent_extension = parent.extension().unwrap_or_default(); + println!( + "Has a parent (they all should), parent is: {:?} and extension is {:?}", + parent.display(), + parent_extension + ); + if parent_extension == "d" { + println!("Parent dir name ends in .d"); + // take file name of parent minus the .d + parent.file_stem().unwrap_or_default().to_string_lossy().to_string() + //let parent_name = parent.file_name().unwrap_or_default().to_string_lossy(); + //parent_name.strip_suffix(".d").unwrap_or_default().to_string() + } else { + // regular parent dir, that means use this file stem + path.file_stem().unwrap_or_default().to_string_lossy().to_string() + } + } + None => { + unreachable!("All configs should have a parent dir") + } + }; + match tokio::fs::read_to_string(entry.path()).await { - Ok(content) => Some(CheckSource::Yaml((entry.path(), content))), + Ok(contents) => Some(CheckSource::Yaml(YamlCheck::new( + check_name, + contents, + Some(entry.path()), + ))), Err(e) => { eprintln!("Error reading file {}: {}", entry.path().display(), e); None @@ -139,6 +173,7 @@ pub struct DirCheckListenerContext { #[cfg(test)] mod tests { use super::*; + use protobuf::descriptor::source_code_info; use tokio::io::AsyncWriteExt; macro_rules! setup_test_dir { @@ -172,17 +207,32 @@ mod tests { "dir.d/file3.yaml" => "sample content", ); - let check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await; - let expected_paths: Vec = vec![ - CheckSource::Yaml((temp_path.join("file1.yaml"), "sample content".to_string())), - CheckSource::Yaml((temp_path.join("file2.yml"), "sample content".to_string())), - CheckSource::Yaml((temp_path.join("dir.d/file3.yaml"), "sample content".to_string())), + let mut expected_sources: Vec = vec![ + CheckSource::Yaml(YamlCheck::new( + "file1", + "sample content", + Some(temp_path.join("file1.yaml")), + )), + CheckSource::Yaml(YamlCheck::new( + "file2", + "sample content", + Some(temp_path.join("file2.yml")), + )), + CheckSource::Yaml(YamlCheck::new( + "dir", + "sample content", + Some(temp_path.join("dir.d/file3.yaml")), + )), ]; - assert_eq!(check_sources.len(), expected_paths.len()); - for source in check_sources { - assert!(expected_paths.contains(&source)); + check_sources.sort(); + expected_sources.sort(); + assert_eq!(check_sources.len(), expected_sources.len()); + + for (idx, source) in check_sources.iter().enumerate() { + assert_eq!(source, &expected_sources[idx]); } } @@ -195,19 +245,31 @@ mod tests { "metrics.yml" => "sample content", "file2yml" => "sample content", "mycheck.d/test.txt" => "sample content", + // THIS TEST FAILS< IT RETURNS FILE3 as name "dir.d/file3.yml" => "sample content", ); - let check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await; - let expected_paths: Vec = vec![ - CheckSource::Yaml((temp_path.join("myched.ck.yaml"), "sample content".to_string())), - CheckSource::Yaml((temp_path.join("dir.d/file3.yml"), "sample content".to_string())), + let mut expected_sources: Vec = vec![ + CheckSource::Yaml(YamlCheck::new( + "myched.ck", + "sample content", + Some(temp_path.join("myched.ck.yaml")), + )), + CheckSource::Yaml(YamlCheck::new( + "dir", + "sample content", + Some(temp_path.join("dir.d/file3.yml")), + )), ]; - assert_eq!(check_sources.len(), expected_paths.len()); - for source in check_sources { - assert!(expected_paths.contains(&source)); + check_sources.sort(); + expected_sources.sort(); + assert_eq!(check_sources.len(), expected_sources.len()); + + for (idx, source) in check_sources.iter().enumerate() { + assert_eq!(source, &expected_sources[idx]); } } @@ -219,16 +281,27 @@ mod tests { "othercheck.yaml.default" => "sample content", ); - let check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await; - let expected_paths: Vec = vec![ - CheckSource::Yaml((temp_path.join("mycheck.yaml"), "sample content".to_string())), - CheckSource::Yaml((temp_path.join("othercheck.yaml.default"), "sample content".to_string())), + let mut expected_sources: Vec = vec![ + CheckSource::Yaml(YamlCheck::new( + "mycheck", + "sample content", + Some(temp_path.join("mycheck.yaml")), + )), + CheckSource::Yaml(YamlCheck::new( + "othercheck", + "sample content", + Some(temp_path.join("othercheck.yaml.default")), + )), ]; - assert_eq!(check_sources.len(), expected_paths.len()); - for source in check_sources { - assert!(expected_paths.contains(&source)); + check_sources.sort(); + expected_sources.sort(); + assert_eq!(check_sources.len(), expected_sources.len()); + + for (idx, source) in check_sources.iter().enumerate() { + assert_eq!(source, &expected_sources[idx]); } } } diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index fb10ad25..a8229dc3 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use async_walkdir::{DirEntry, Filtering, WalkDir}; +use async_walkdir::{Filtering, WalkDir}; use futures::StreamExt as _; use memory_accounting::{MemoryBounds, MemoryBoundsBuilder}; use pyo3::prelude::*; @@ -88,15 +88,6 @@ impl Display for CheckRequest { } } -impl Into for CheckRequest { - fn into(self) -> RunnableCheckRequest { - RunnableCheckRequest { - check_request: self, - check_source_code: None, - } - } -} - impl CheckRequest { fn to_runnable_request(&self) -> Result { Ok(RunnableCheckRequest { @@ -132,6 +123,15 @@ impl Display for RunnableCheckRequest { } } +impl From for RunnableCheckRequest { + fn from(check_request: CheckRequest) -> Self { + Self { + check_request, + check_source_code: None, + } + } +} + #[derive(Debug, Deserialize)] struct YamlCheckConfiguration { name: Option, @@ -214,15 +214,41 @@ fn default_min_collection_interval_ms() -> u32 { 15_000 } -#[derive(Debug, Clone, Eq, Hash, PartialEq)] +#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] +struct YamlCheck { + found_path: Option, + name: String, + contents: String, +} + +impl YamlCheck { + pub fn new(name: N, contents: C, found_path: Option) -> YamlCheck + where + N: Into, + C: Into, + { + Self { + name: name.into(), + contents: contents.into(), + found_path, + } + } +} + +#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] enum CheckSource { - Yaml((PathBuf, String)), + Yaml(YamlCheck), } impl CheckSource { fn to_check_request(&self) -> Result { match self { - CheckSource::Yaml((path, contents)) => { + CheckSource::Yaml(y) => { + let YamlCheck { + name, + contents, + found_path: _, + } = y; let mut checks_config = Vec::new(); let read_yaml: YamlCheckConfiguration = match serde_yaml::from_str(contents) { Ok(read) => read, @@ -255,25 +281,15 @@ impl CheckSource { checks_config.push(CheckInstanceConfiguration(map)); } - // TODO this does not support the './http_check.d/foo.yaml' pattern - // DirCheckListener needs to be updated to support that format as well. - let file_stem_name = path - .file_stem() - .expect("File name must have a stem") - .to_string_lossy() - .to_string(); let check_name = match &read_yaml.name { Some(yaml_name) => { - if *yaml_name != file_stem_name { - warn!( - "Name in yaml file does not match file name: {} != {}", - yaml_name, file_stem_name - ); + if yaml_name != name { + warn!("Name in yaml file does not match file name: {} != {}", yaml_name, name); warn!("Using 'name' field value as the loadable_name."); } yaml_name.clone() } - None => file_stem_name.clone(), + None => name.clone(), }; Ok(CheckRequest { name: check_name, @@ -289,7 +305,11 @@ impl CheckSource { impl Display for CheckSource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - CheckSource::Yaml((path, contents)) => write!(f, "{contents} from file: {}", path.display()), + CheckSource::Yaml(YamlCheck { + name, + contents: _, + found_path: _, + }) => write!(f, "YamlCheckName: {name}"), } } } diff --git a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs index 02362d1b..695161fa 100644 --- a/lib/saluki-components/src/sources/checks/python_exposed_modules.rs +++ b/lib/saluki-components/src/sources/checks/python_exposed_modules.rs @@ -1,4 +1,4 @@ -use tracing::{event, trace}; +use tracing::trace; use super::*; diff --git a/lib/saluki-components/src/sources/checks/python_scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs index 195f1817..ff92a27f 100644 --- a/lib/saluki-components/src/sources/checks/python_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -360,7 +360,11 @@ class MyCheck(AgentCheck): def check(self, instance): self.gauge('test-metric-name', 41, tags=['hello:world'])"#; - let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let source = CheckSource::Yaml(YamlCheck::new( + "my_check", + "instances: [{}]", + Some(PathBuf::from("/tmp/my_check.yaml")), + )); let check_request = source.to_check_request().unwrap(); let runnable_check_request = RunnableCheckRequest { @@ -393,7 +397,11 @@ class MyCheck(AgentCheck): def check(self, instance): self.gauge('test-metric-name', 41, tags=['hello:world'])"#; - let source = CheckSource::Yaml((PathBuf::from("/tmp/my_check.yaml"), "instances: [{}]".to_string())); + let source = CheckSource::Yaml(YamlCheck::new( + "my_check", + "instances: [{}]", + Some(PathBuf::from("/tmp/my_check.yaml")), + )); let check_request = source.to_check_request().unwrap(); let runnable_check_request = RunnableCheckRequest { From 7eee9d94801fd79b94d8632256b2ddc97acccc96 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 24 May 2024 16:43:45 +0000 Subject: [PATCH 42/47] Remove experimental core check and wasm check schedulers --- .../src/sources/checks/corecheck_scheduler.rs | 174 ------------------ .../src/sources/checks/mod.rs | 28 +-- .../src/sources/checks/wasmcheck_scheduler.rs | 142 -------------- 3 files changed, 1 insertion(+), 343 deletions(-) delete mode 100644 lib/saluki-components/src/sources/checks/corecheck_scheduler.rs delete mode 100644 lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs diff --git a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs b/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs deleted file mode 100644 index 61e3b3a5..00000000 --- a/lib/saluki-components/src/sources/checks/corecheck_scheduler.rs +++ /dev/null @@ -1,174 +0,0 @@ -use tokio::task::JoinHandle; - -use super::*; - -struct CheckHandle(JoinHandle>); - -#[derive(Debug, Clone, Copy)] -enum CoreChecks { - Smoke(SmokeCheck), -} - -#[derive(Debug, Clone, Copy)] -struct SmokeCheck {} -impl SmokeCheck { - const NAME: &'static str = "smoke"; - - fn run( - &self, send_metrics: mpsc::Sender, config: CheckInstanceConfiguration, - ) -> JoinHandle> { - debug!("Beginning execution of Smoke Check Instance {:p}", self); - tokio::spawn(async move { - loop { - send_metrics - .send(CheckMetric { - name: "smoke_metric".to_string(), - metric_type: PyMetricType::Gauge, - value: 1.0, - tags: vec!["hello:world".to_string()], - }) - .await - .map_err(|e| generic_error!("Failed to send metric: {}", e))?; - - tokio::time::sleep(Duration::from_millis(config.min_collection_interval_ms().into())).await; - } - }) - } -} - -impl CoreChecks { - fn from_name(name: &str) -> Option { - match name { - SmokeCheck::NAME => Some(CoreChecks::Smoke(SmokeCheck {})), - _ => None, - } - } - - fn name(&self) -> &'static str { - match self { - CoreChecks::Smoke(_) => SmokeCheck::NAME, - } - } - - fn run( - self, send_metrics: mpsc::Sender, config: CheckInstanceConfiguration, - ) -> JoinHandle> { - match self { - CoreChecks::Smoke(check) => check.run(send_metrics, config), - } - } -} - -pub struct CoreCheckScheduler { - send_check_metrics: mpsc::Sender, - running: HashMap>, -} - -impl CoreCheckScheduler { - pub fn new(send_check_metrics: mpsc::Sender) -> Result { - Ok(CoreCheckScheduler { - send_check_metrics, - running: HashMap::new(), - }) - } -} - -impl CheckScheduler for CoreCheckScheduler { - fn can_run_check(&self, runnable: &RunnableCheckRequest) -> bool { - info!( - "Considered whether to run check as corecheck: {:?}", - runnable.check_request - ); - CoreChecks::from_name(&runnable.check_request.name).is_some() - } - fn run_check(&mut self, runnable: &RunnableCheckRequest) -> Result<(), GenericError> { - let check = match CoreChecks::from_name(&runnable.check_request.name) { - Some(check) => check, - None => return Err(generic_error!("Check is not supported, can't run it.")), - }; - info!( - "Running {} instances of check {}", - runnable.check_request.instances.len(), - check.name() - ); - for instance in &runnable.check_request.instances { - let handle = check.run(self.send_check_metrics.clone(), instance.clone()); - self.running - .entry(runnable.check_request.source.clone()) - .or_default() - .push(CheckHandle(handle)); - } - - Ok(()) - } - fn stop_check(&mut self, check_name: CheckRequest) { - let removed = self.running.remove(&check_name.source); - if let Some(removed) = removed { - for handle in removed { - handle.0.abort(); - } - } else { - warn!("Tried to stop a check that wasn't running") - } - } -} - -#[cfg(test)] -mod tests { - use super::super::*; - use super::*; - use tokio::sync::mpsc; - - #[tokio::test] - async fn test_new_check_scheduler() { - let (sender, _) = mpsc::channel(10); - let scheduler = CoreCheckScheduler::new(sender).unwrap(); - assert!(scheduler.running.is_empty()); - } - - #[tokio::test] - async fn test_can_run_with_no_checks() { - let (sender, _) = mpsc::channel(10); - let scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml(YamlCheck::new("my_check", "instances: [{}]", None)); - let check_request = source.to_check_request().unwrap(); - - let runnable_check_request: RunnableCheckRequest = check_request.into(); - assert!(!scheduler.can_run_check(&runnable_check_request)); - } - - #[tokio::test] - async fn test_can_run_with_valid_check() { - let (sender, _) = mpsc::channel(10); - let scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml(YamlCheck::new("smoke", "instances: [{}]", None)); - let check_request = source.to_check_request().unwrap(); - - let runnable_check_request: RunnableCheckRequest = check_request.into(); - assert!(scheduler.can_run_check(&runnable_check_request)); - } - - #[tokio::test] - async fn test_execution_with_single_instance() { - let (sender, mut receiver) = mpsc::channel(10); - let mut scheduler = CoreCheckScheduler::new(sender).unwrap(); - let source = CheckSource::Yaml(YamlCheck::new("smoke", "instances: [{}]", None)); - let check_request = source.to_check_request().unwrap(); - - let runnable_check_request: RunnableCheckRequest = check_request.into(); - scheduler - .run_check(&runnable_check_request) - .expect("Failed to run check"); - - assert_eq!(scheduler.running.len(), 1); - - let check_metric = CheckMetric { - name: "smoke_metric".to_string(), - metric_type: PyMetricType::Gauge, - value: 1.0, - tags: vec!["hello:world".to_string()], - }; - let check_from_channel = receiver.recv().await.unwrap(); - assert_eq!(check_from_channel, check_metric); - } -} diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index a8229dc3..4ce2157b 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -25,11 +25,9 @@ use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; use tracing::{debug, error, info, warn}; -mod corecheck_scheduler; mod listener; mod python_exposed_modules; mod python_scheduler; -mod wasmcheck_scheduler; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] @@ -355,8 +353,6 @@ impl MemoryBounds for ChecksConfiguration { struct CheckDispatcher { python_scheduler: python_scheduler::PythonCheckScheduler, - corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler, - wasmcheck_scheduler: wasmcheck_scheduler::WasmCheckScheduler, check_metrics_rx: mpsc::Receiver, check_run_requests: mpsc::Receiver, check_stop_requests: mpsc::Receiver, @@ -372,8 +368,6 @@ impl CheckDispatcher { check_run_requests, check_stop_requests, python_scheduler: python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone())?, - corecheck_scheduler: corecheck_scheduler::CoreCheckScheduler::new(check_metrics_tx.clone())?, - wasmcheck_scheduler: wasmcheck_scheduler::WasmCheckScheduler::new(check_metrics_tx.clone())?, }) } @@ -387,8 +381,6 @@ impl CheckDispatcher { mut check_run_requests, mut check_stop_requests, mut python_scheduler, - mut corecheck_scheduler, - mut wasmcheck_scheduler, check_metrics_rx, } = self; tokio::spawn(async move { @@ -396,25 +388,7 @@ impl CheckDispatcher { select! { Some(check_request) = check_run_requests.recv() => { info!("Dispatching check request: {}", check_request); - if corecheck_scheduler.can_run_check(&check_request) { - match corecheck_scheduler.run_check(&check_request) { - Ok(_) => { - debug!("Check request dispatched: {}", check_request); - } - Err(e) => { - error!("Error dispatching check request: {}", e); - } - } - } else if wasmcheck_scheduler.can_run_check(&check_request) { - match wasmcheck_scheduler.run_check(&check_request) { - Ok(_) => { - debug!("Check request dispatched: {}", check_request); - } - Err(e) => { - error!("Error dispatching check request: {}", e); - } - } - } else if python_scheduler.can_run_check(&check_request) { + if python_scheduler.can_run_check(&check_request) { match python_scheduler.run_check(&check_request) { Ok(_) => { debug!("Check request dispatched: {}", check_request); diff --git a/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs b/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs deleted file mode 100644 index 253bbbbf..00000000 --- a/lib/saluki-components/src/sources/checks/wasmcheck_scheduler.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::*; - -use wasmtime::component::ResourceTable; -use wasmtime::Store; -use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; - -struct MyState { - ctx: WasiCtx, - table: ResourceTable, - check_sender: mpsc::Sender, -} - -impl WasiView for MyState { - fn ctx(&mut self) -> &mut WasiCtx { - &mut self.ctx - } - fn table(&mut self) -> &mut ResourceTable { - &mut self.table - } -} - -struct CheckHandle(u32); - -pub struct WasmCheckScheduler { - linker: wasmtime::component::Linker, - engine: wasmtime::Engine, - store: Store, - running: HashMap>, - available_components: HashMap, -} - -fn find_available_wasm_checks() -> Result, GenericError> { - let mut available_checks = vec![]; - let wasm_files = std::fs::read_dir("./dist/checks.d/")?; - for entry in wasm_files { - let entry = entry?; - debug!("Considering file: {:?}", entry); - let path = entry.path(); - if let Some(extension) = path.extension() { - if extension == "wasm" { - available_checks.push(path); - } - } - } - Ok(available_checks) -} - -impl WasmCheckScheduler { - pub fn new(send_check_metrics: mpsc::Sender) -> Result { - let engine = wasmtime::Engine::default(); - - let mut linker = wasmtime::component::Linker::::new(&engine); - wasmtime_wasi::add_to_linker_sync(&mut linker)?; - - let mut wasi_builder = WasiCtxBuilder::new(); - - let mut store = Store::new( - &engine, - MyState { - ctx: wasi_builder.build(), - table: ResourceTable::new(), - check_sender: send_check_metrics, - }, - ); - // TODO - once I have this version working, move to the `wasmtime::component::bindgen` - // https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html - // this will let me implement the imports as a trait. - type Params = (String, f64, Vec); - linker.root().func_wrap("reportmetric", |store, params: Params| { - let (metricname, value, tags) = params; - println!("reportmetric called: {:?}, {:?}, {:?}", metricname, value, tags); - match store.data().check_sender.try_send(CheckMetric { - name: metricname, - metric_type: PyMetricType::Gauge, - value, - tags, - }) { - Ok(_) => {} - Err(e) => { - error!("Error sending check metric: {:?}", e); - } - } - Ok(()) - })?; - - let mut available_components = HashMap::new(); - let components_on_disk = find_available_wasm_checks()?; - for component_path in components_on_disk { - let bytes = std::fs::read(&component_path)?; - // TODO how to get a name for this component? - // Could it be statically available on the component? idk enough about WIT / Components to say - // For now, its the wasm file name - let component_name = component_path.file_stem().unwrap().to_string_lossy().to_string(); - - let component = wasmtime::component::Component::new(&engine, bytes)?; - let instance = linker.instantiate(&mut store, &component)?; - available_components.insert(component_name, instance); - } - info!( - "Available components are: {:?}", - available_components.keys().collect::>() - ); - - Ok(WasmCheckScheduler { - linker, - engine, - store, - available_components, - running: HashMap::new(), - }) - } -} - -impl CheckScheduler for WasmCheckScheduler { - fn can_run_check(&self, check_request: &RunnableCheckRequest) -> bool { - info!( - "Considered whether to run check as wasmcheck: {:?}", - check_request.check_request - ); - self.available_components - .contains_key(&check_request.check_request.name) - } - - // Currently just runs the 'run' function once, no repeated execution, no instance config, etc. - fn run_check(&mut self, _check_request: &RunnableCheckRequest) -> Result<(), GenericError> { - let component = self - .available_components - .get(&_check_request.check_request.name) - .ok_or(generic_error!("Component not found, can't load wasm check"))?; - - let func = component - .get_func(&mut self.store, "run") - .expect("run export not found"); - let mut result = []; - func.call(&mut self.store, &[], &mut result)?; - Ok(()) - } - - fn stop_check(&mut self, _check_request: CheckRequest) { - // todo - } -} From c2fa865a40c557a88a251be459ee355f476e97f7 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 24 May 2024 17:42:16 +0000 Subject: [PATCH 43/47] Rewrites yaml searching logic again and cleans up some error handling/logging --- .../src/sources/checks/listener.rs | 183 +++++++++--------- .../src/sources/checks/mod.rs | 30 +-- .../src/sources/checks/python_scheduler.rs | 16 +- 3 files changed, 110 insertions(+), 119 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index 6a5124a4..23d155f8 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -1,7 +1,9 @@ +use tokio::fs; + use super::*; pub struct DirCheckRequestListener { - pub base_path: PathBuf, + pub search_paths: Vec, pub known_check_requests: Vec, // These could all be oneshot channels I think @@ -13,6 +15,37 @@ pub struct DirCheckRequestListener { } impl DirCheckRequestListener { + pub fn from_paths>(paths: Vec

) -> Result { + for p in paths.iter() { + if !p.as_ref().exists() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), + }); + } + if !p.as_ref().is_dir() { + return Err(Error::DirectoryIncorrect { + source: io::Error::new( + io::ErrorKind::NotFound, + format!("Path {} is not a directory", p.as_ref().display()), + ), + }); + } + } + + let search_paths = paths.iter().map(|p| p.as_ref().to_path_buf()).collect(); + + let (new_paths_tx, new_paths_rx) = mpsc::channel(100); + let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); + Ok(DirCheckRequestListener { + search_paths, + known_check_requests: Vec::new(), + new_path_tx: new_paths_tx, + deleted_path_tx: deleted_paths_tx, + new_path_rx: Some(new_paths_rx), + deleted_path_rx: Some(deleted_paths_rx), + }) + } + /// Constructs a new `Listener` that will monitor the specified path. pub fn from_path>(path: P) -> Result { let path_ref = path.as_ref(); @@ -30,7 +63,7 @@ impl DirCheckRequestListener { let (new_paths_tx, new_paths_rx) = mpsc::channel(100); let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); Ok(DirCheckRequestListener { - base_path: path.as_ref().to_path_buf(), + search_paths: vec![path.as_ref().to_path_buf()], known_check_requests: Vec::new(), new_path_tx: new_paths_tx, deleted_path_tx: deleted_paths_tx, @@ -47,8 +80,8 @@ impl DirCheckRequestListener { (self.new_path_rx.take().unwrap(), self.deleted_path_rx.take().unwrap()) } - pub async fn update_check_entities(&mut self) -> Result<(), Error> { - let new_check_paths = self.get_check_entities().await; + pub async fn update_check_entities(&mut self) -> Result<(), GenericError> { + let new_check_paths = self.get_check_entities().await?; let current_paths = self.known_check_requests.iter().map(|e| e.source.clone()); let current: HashSet = HashSet::from_iter(current_paths); let new: HashSet = HashSet::from_iter(new_check_paths.iter().cloned()); @@ -75,91 +108,62 @@ impl DirCheckRequestListener { Ok(()) } - /// Retrieves all check entities from the base path that match the required check formats. - pub async fn get_check_entities(&self) -> Vec { - let entries = WalkDir::new(&self.base_path).filter(|entry| async move { - let file_type = entry.file_type().await.expect("Error getting file type"); - let mut path = entry.path(); - let mut extension = path.extension().unwrap_or_default(); - // JMX checks offer a `metrics.yaml`/`metrics.yml` - // we don't care about these, ignore them. - if path.to_string_lossy().ends_with("metrics.yaml") || path.to_string_lossy().ends_with("metrics.yml") { - return Filtering::Ignore; - } - - if file_type.is_file() && extension == "default" { - // is default entry - path.set_extension(""); // trim off '.default' - extension = path.extension().unwrap_or_default(); // update extension - } - let is_d_dir = file_type.is_dir() && path.to_string_lossy().ends_with('d'); - let is_yaml_file = file_type.is_file() && (extension == "yaml" || extension == "yml"); - if is_d_dir || is_yaml_file { - Filtering::Continue - } else { - Filtering::Ignore + async fn consider_file(&self, original_path: PathBuf, provided_check_name: Option) -> Option { + let mut path = original_path.clone(); + let mut extension = path.extension().unwrap_or_default().to_owned(); + if extension == "default" { + // is default entry + path.set_extension(""); // trim off '.default' + extension = path.extension().unwrap_or_default().to_owned() + } + if path.ends_with("metrics.yaml") || path.ends_with("metrics.yml") { + // 'metrics.yaml' is for JMX checks. we don't care about them here + return None; + } + if extension == "yaml" || extension == "yml" { + // is yaml file + let check_name: String = provided_check_name + .unwrap_or_else(|| path.file_stem().unwrap_or_default().to_string_lossy().to_string()); + match fs::read_to_string(&original_path).await { + Ok(contents) => { + let check = YamlCheck::new(check_name, contents, Some(original_path.to_path_buf())); + Some(CheckSource::Yaml(check)) + } + Err(e) => { + eprintln!("Error reading file {}: {}", original_path.display(), e); + None + } } - }); - - entries - .filter_map(|e| async move { - match e { - Ok(entry) => { - if entry.path().is_dir() { - return None; - } - let mut path = entry.path().clone(); - - println!("Processing path: {:?}", path.display()); - let extension = path.extension().unwrap_or_default(); - if path.is_file() && extension == "default" { - // is default entry - path.set_extension(""); // trim off '.default' - } - let check_name: String = match path.parent() { - Some(parent) => { - let parent_extension = parent.extension().unwrap_or_default(); - println!( - "Has a parent (they all should), parent is: {:?} and extension is {:?}", - parent.display(), - parent_extension - ); - if parent_extension == "d" { - println!("Parent dir name ends in .d"); - // take file name of parent minus the .d - parent.file_stem().unwrap_or_default().to_string_lossy().to_string() - //let parent_name = parent.file_name().unwrap_or_default().to_string_lossy(); - //parent_name.strip_suffix(".d").unwrap_or_default().to_string() - } else { - // regular parent dir, that means use this file stem - path.file_stem().unwrap_or_default().to_string_lossy().to_string() - } - } - None => { - unreachable!("All configs should have a parent dir") - } - }; - - match tokio::fs::read_to_string(entry.path()).await { - Ok(contents) => Some(CheckSource::Yaml(YamlCheck::new( - check_name, - contents, - Some(entry.path()), - ))), - Err(e) => { - eprintln!("Error reading file {}: {}", entry.path().display(), e); - None - } + } else { + None + } + } + /// Retrieves all check entities from the base path that match the required check formats. + pub async fn get_check_entities(&self) -> Result, GenericError> { + let mut sources = vec![]; + for p in self.search_paths.iter() { + let mut entries = fs::read_dir(p).await?; + while let Ok(Some(entry)) = entries.next_entry().await { + let path = entry.path(); + let extension = path.extension().unwrap_or_default(); + let file_type = entry.file_type().await?; + if file_type.is_dir() && extension == "d" { + let module_name = path.file_stem().unwrap_or_default().to_string_lossy().to_string(); + let mut sub_entries = fs::read_dir(path).await?; + while let Ok(Some(sub_entry)) = sub_entries.next_entry().await { + let path = sub_entry.path(); + if let Some(source) = self.consider_file(path, Some(module_name.clone())).await { + sources.push(source); } } - Err(e) => { - eprintln!("Error traversing files: {}", e); - None + } else if file_type.is_file() { + if let Some(source) = self.consider_file(path, None).await { + sources.push(source); } } - }) - .collect() - .await + } + } + Ok(sources) } } @@ -173,7 +177,6 @@ pub struct DirCheckListenerContext { #[cfg(test)] mod tests { use super::*; - use protobuf::descriptor::source_code_info; use tokio::io::AsyncWriteExt; macro_rules! setup_test_dir { @@ -207,7 +210,7 @@ mod tests { "dir.d/file3.yaml" => "sample content", ); - let mut check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await.unwrap(); let mut expected_sources: Vec = vec![ CheckSource::Yaml(YamlCheck::new( @@ -229,7 +232,6 @@ mod tests { check_sources.sort(); expected_sources.sort(); - assert_eq!(check_sources.len(), expected_sources.len()); for (idx, source) in check_sources.iter().enumerate() { assert_eq!(source, &expected_sources[idx]); @@ -245,11 +247,10 @@ mod tests { "metrics.yml" => "sample content", "file2yml" => "sample content", "mycheck.d/test.txt" => "sample content", - // THIS TEST FAILS< IT RETURNS FILE3 as name "dir.d/file3.yml" => "sample content", ); - let mut check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await.unwrap(); let mut expected_sources: Vec = vec![ CheckSource::Yaml(YamlCheck::new( @@ -266,7 +267,6 @@ mod tests { check_sources.sort(); expected_sources.sort(); - assert_eq!(check_sources.len(), expected_sources.len()); for (idx, source) in check_sources.iter().enumerate() { assert_eq!(source, &expected_sources[idx]); @@ -281,7 +281,7 @@ mod tests { "othercheck.yaml.default" => "sample content", ); - let mut check_sources = listener.get_check_entities().await; + let mut check_sources = listener.get_check_entities().await.unwrap(); let mut expected_sources: Vec = vec![ CheckSource::Yaml(YamlCheck::new( @@ -298,7 +298,6 @@ mod tests { check_sources.sort(); expected_sources.sort(); - assert_eq!(check_sources.len(), expected_sources.len()); for (idx, source) in check_sources.iter().enumerate() { assert_eq!(source, &expected_sources[idx]); diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index 4ce2157b..b2f179a2 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,6 +1,4 @@ use async_trait::async_trait; -use async_walkdir::{Filtering, WalkDir}; -use futures::StreamExt as _; use memory_accounting::{MemoryBounds, MemoryBoundsBuilder}; use pyo3::prelude::*; use saluki_config::GenericConfiguration; @@ -23,7 +21,7 @@ use std::{ use std::{collections::HashSet, io}; use std::{fmt::Display, time::Duration}; use tokio::{select, sync::mpsc}; -use tracing::{debug, error, info, warn}; +use tracing::{debug, error, info, trace, warn}; mod listener; mod python_exposed_modules; @@ -87,13 +85,6 @@ impl Display for CheckRequest { } impl CheckRequest { - fn to_runnable_request(&self) -> Result { - Ok(RunnableCheckRequest { - check_request: self.clone(), - check_source_code: None, - }) - } - // Ideally this matches // https://github.com/DataDog/datadog-agent/blob/b039ea43d3168f521e8ea3e8356a0e84eec170d1/comp/core/autodiscovery/integration/config.go#L362 // but for now, lets just do this @@ -387,8 +378,8 @@ impl CheckDispatcher { loop { select! { Some(check_request) = check_run_requests.recv() => { - info!("Dispatching check request: {}", check_request); if python_scheduler.can_run_check(&check_request) { + info!("Dispatching to Python {}", check_request); match python_scheduler.run_check(&check_request) { Ok(_) => { debug!("Check request dispatched: {}", check_request); @@ -509,7 +500,7 @@ impl Source for Checks { } async fn process_listener( - source_context: SourceContext, listener_context: listener::DirCheckListenerContext, + _source_context: SourceContext, listener_context: listener::DirCheckListenerContext, ) -> Result<(), GenericError> { let listener::DirCheckListenerContext { shutdown_handle, @@ -526,7 +517,7 @@ async fn process_listener( loop { select! { _ = &mut shutdown_handle => { - debug!("Received shutdown signal. Waiting for existing stream handlers to finish..."); + info!("Received shutdown signal. Shutting down check listeners."); break; } _ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => { @@ -538,17 +529,12 @@ async fn process_listener( } } Some(new_entity) = new_entities.recv() => { - let check_request = match new_entity.to_runnable_request() { - Ok(check_request) => check_request, - Err(e) => { - error!("Can't convert check source to runnable request: {}", e); - continue; - } - }; + let runnable_check_request: RunnableCheckRequest = new_entity.into(); + info!("New Check request for {} received.", runnable_check_request.check_request.name); - match submit_runnable_check_req.send(check_request).await { + match submit_runnable_check_req.send(runnable_check_request).await { Ok(_) => { - debug!("Check request submitted to dispatcher"); + trace!("Check request submitted to dispatcher"); } Err(e) => { error!("Error running check: {}", e); diff --git a/lib/saluki-components/src/sources/checks/python_scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs index ff92a27f..85f2adc9 100644 --- a/lib/saluki-components/src/sources/checks/python_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -129,20 +129,25 @@ impl PythonCheckScheduler { if let Some(py_source) = &check.check_source_code { return self.register_check_with_source(py_source.clone()); } + let mut load_errors = vec![]; for import_str in &[&check_module_name, &&format!("datadog_checks.{}", check_module_name)] { match self.register_check_from_imports(import_str) { Ok(handle) => return Ok(handle), Err(e) => { - error!(%e, "Could not find check {check_module_name} from imports"); + // Not an error yet, wait until all imports have been tried + // Grab the underlying error, not the anyhow wrapper + load_errors.push(e.root_cause().to_string()); } } } - Err(generic_error!("Could not find class for check {check_module_name}")) + + Err(generic_error!( + "Could not find class for check {check_module_name}. Errors: {load_errors:?}" + )) } - fn register_check_from_imports(&mut self, import_path: &str) -> Result { + fn register_check_from_imports(&self, import_path: &str) -> Result { pyo3::Python::with_gil(|py| { - debug!("Imported '{import_path}', checking its exports for subclasses of 'AgentCheck'"); let module = py.import_bound(import_path)?; let base_class = self.agent_check_base_class.bind(py); @@ -229,7 +234,8 @@ impl PythonCheckScheduler { } impl CheckScheduler for PythonCheckScheduler { fn can_run_check(&self, _check: &RunnableCheckRequest) -> bool { - true + self.register_check_from_imports(_check.check_request.name.as_str()) + .is_ok() } // This function does 3 things From 50831269b0b45ac681ea2bc5377781827cc953c9 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 24 May 2024 20:48:49 +0000 Subject: [PATCH 44/47] Removes un-used constructor --- .../src/sources/checks/listener.rs | 60 +++++-------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index 23d155f8..a19ffc9f 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -15,24 +15,22 @@ pub struct DirCheckRequestListener { } impl DirCheckRequestListener { + /// Constructs a new `Listener` that will monitor the specified paths. pub fn from_paths>(paths: Vec

) -> Result { - for p in paths.iter() { - if !p.as_ref().exists() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), - }); - } - if !p.as_ref().is_dir() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new( - io::ErrorKind::NotFound, - format!("Path {} is not a directory", p.as_ref().display()), - ), - }); - } - } - - let search_paths = paths.iter().map(|p| p.as_ref().to_path_buf()).collect(); + let search_paths: Vec = paths + .iter() + .filter_map(|p| { + if !p.as_ref().exists() { + warn!("Skipping path '{}' as it does not exist", p.as_ref().display()); + return None; + } + if !p.as_ref().is_dir() { + warn!("Skipping path '{}', it is not a directory.", p.as_ref().display()); + return None; + } + Some(p.as_ref().to_path_buf()) + }) + .collect(); let (new_paths_tx, new_paths_rx) = mpsc::channel(100); let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); @@ -46,32 +44,6 @@ impl DirCheckRequestListener { }) } - /// Constructs a new `Listener` that will monitor the specified path. - pub fn from_path>(path: P) -> Result { - let path_ref = path.as_ref(); - if !path_ref.exists() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new(io::ErrorKind::NotFound, "Path does not exist"), - }); - } - if !path_ref.is_dir() { - return Err(Error::DirectoryIncorrect { - source: io::Error::new(io::ErrorKind::NotFound, "Path is not a directory"), - }); - } - - let (new_paths_tx, new_paths_rx) = mpsc::channel(100); - let (deleted_paths_tx, deleted_paths_rx) = mpsc::channel(100); - Ok(DirCheckRequestListener { - search_paths: vec![path.as_ref().to_path_buf()], - known_check_requests: Vec::new(), - new_path_tx: new_paths_tx, - deleted_path_tx: deleted_paths_tx, - new_path_rx: Some(new_paths_rx), - deleted_path_rx: Some(deleted_paths_rx), - }) - } - pub fn subscribe(&mut self) -> (mpsc::Receiver, mpsc::Receiver) { if self.new_path_rx.is_none() || self.deleted_path_rx.is_none() { panic!("Invariant violated: subscribe called after consuming the receivers"); @@ -195,7 +167,7 @@ mod tests { file.write_all($content.as_bytes()).await.expect("file write"); )* - let listener = DirCheckRequestListener::from_path(&temp_path).unwrap(); + let listener = DirCheckRequestListener::from_paths(vec![&temp_path]).unwrap(); (listener, temp_path.to_path_buf(), temp_dir) }}; } From 45d97c41536ab04ac8594a99a009e5a1129b108c Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 24 May 2024 20:49:17 +0000 Subject: [PATCH 45/47] Adds basic telemetry into checks running --- .../src/sources/checks/mod.rs | 48 +++++++++++++++---- .../src/sources/checks/python_scheduler.rs | 41 ++++++++++++---- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/lib/saluki-components/src/sources/checks/mod.rs b/lib/saluki-components/src/sources/checks/mod.rs index b2f179a2..26d1cbd8 100644 --- a/lib/saluki-components/src/sources/checks/mod.rs +++ b/lib/saluki-components/src/sources/checks/mod.rs @@ -1,9 +1,10 @@ use async_trait::async_trait; use memory_accounting::{MemoryBounds, MemoryBoundsBuilder}; +use metrics::Counter; use pyo3::prelude::*; use saluki_config::GenericConfiguration; use saluki_core::{ - components::{Source, SourceBuilder, SourceContext}, + components::{metrics::MetricsBuilder, Source, SourceBuilder, SourceContext}, topology::{ shutdown::{ComponentShutdownHandle, DynamicShutdownCoordinator, DynamicShutdownHandle}, OutputDefinition, @@ -50,12 +51,16 @@ enum Error { #[derive(Deserialize)] pub struct ChecksConfiguration { /// The directory containing the check configurations. - #[serde(default = "default_check_config_dir")] - check_config_dir: String, + #[serde(default = "default_check_config_dirs")] + check_config_dirs: Vec, } -fn default_check_config_dir() -> String { - "./dist/conf.d".to_string() +fn default_check_config_dirs() -> Vec { + vec![ + "./dist/conf.d".to_string(), + "/etc/datadog-agent/conf.d".to_string(), + "etc-datadog-agent/conf.d".to_string(), + ] } #[derive(Debug, Clone)] @@ -312,7 +317,7 @@ impl ChecksConfiguration { async fn build_listeners(&self) -> Result, Error> { let mut listeners = Vec::new(); - let listener = listener::DirCheckRequestListener::from_path(&self.check_config_dir)?; + let listener = listener::DirCheckRequestListener::from_paths(self.check_config_dirs.clone())?; listeners.push(listener); @@ -343,6 +348,7 @@ impl MemoryBounds for ChecksConfiguration { } struct CheckDispatcher { + tlm: ChecksTelemetry, python_scheduler: python_scheduler::PythonCheckScheduler, check_metrics_rx: mpsc::Receiver, check_run_requests: mpsc::Receiver, @@ -352,13 +358,16 @@ struct CheckDispatcher { impl CheckDispatcher { fn new( check_run_requests: mpsc::Receiver, check_stop_requests: mpsc::Receiver, + tlm: ChecksTelemetry, ) -> Result { let (check_metrics_tx, check_metrics_rx) = mpsc::channel(10_000_000); + let python_scheduler = python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone(), tlm.clone())?; Ok(Self { + tlm, check_metrics_rx, check_run_requests, check_stop_requests, - python_scheduler: python_scheduler::PythonCheckScheduler::new(check_metrics_tx.clone())?, + python_scheduler, }) } @@ -369,6 +378,7 @@ impl CheckDispatcher { info!("Check dispatcher started."); let CheckDispatcher { + tlm, mut check_run_requests, mut check_stop_requests, mut python_scheduler, @@ -382,6 +392,7 @@ impl CheckDispatcher { info!("Dispatching to Python {}", check_request); match python_scheduler.run_check(&check_request) { Ok(_) => { + tlm.check_requests_dispatched.increment(1); debug!("Check request dispatched: {}", check_request); } Err(e) => { @@ -405,6 +416,21 @@ impl CheckDispatcher { } } +#[derive(Clone)] +pub struct ChecksTelemetry { + check_requests_dispatched: Counter, + check_instances_started: Counter, +} + +impl ChecksTelemetry { + fn noop() -> Self { + Self { + check_requests_dispatched: Counter::noop(), + check_instances_started: Counter::noop(), + } + } +} + pub struct Checks { listeners: Vec, } @@ -413,12 +439,18 @@ impl Checks { // consumes self async fn run_inner(self, context: SourceContext, global_shutdown: ComponentShutdownHandle) -> Result<(), ()> { let mut listener_shutdown_coordinator = DynamicShutdownCoordinator::default(); + let metrics = MetricsBuilder::from_component_context(context.component_context()); + let tlm = ChecksTelemetry { + check_requests_dispatched: metrics.register_counter("checkrequests.dispatched"), + check_instances_started: metrics.register_counter("checkinstances.started"), + }; let Checks { listeners } = self; let (check_run_requests_tx, check_run_requests_rx) = mpsc::channel(100); let (check_stop_requests_tx, check_stop_requests_rx) = mpsc::channel(100); - let dispatcher = CheckDispatcher::new(check_run_requests_rx, check_stop_requests_rx).expect("Could create"); + let dispatcher = + CheckDispatcher::new(check_run_requests_rx, check_stop_requests_rx, tlm).expect("Could create"); let mut joinset = tokio::task::JoinSet::new(); // Run the local task set. diff --git a/lib/saluki-components/src/sources/checks/python_scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs index 85f2adc9..ba6f94d6 100644 --- a/lib/saluki-components/src/sources/checks/python_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -22,12 +22,13 @@ pub struct PythonSenderHolder { } pub struct PythonCheckScheduler { + tlm: ChecksTelemetry, running: HashMap>)>, agent_check_base_class: Py, } impl PythonCheckScheduler { - pub fn new(send_check_metrics: mpsc::Sender) -> Result { + pub fn new(send_check_metrics: mpsc::Sender, tlm: ChecksTelemetry) -> Result { pyo3::append_to_inittab!(datadog_agent); pyo3::append_to_inittab!(pyagg); @@ -95,6 +96,7 @@ impl PythonCheckScheduler { })?; Ok(Self { + tlm, running: HashMap::new(), agent_check_base_class: agent_check_base_class.expect("AgentCheck class should be present"), }) @@ -116,13 +118,20 @@ impl PythonCheckScheduler { // - `AgentCheck.load_config(instance)` // JK load_config is just yaml parsing -- str -> pyAny - // which I don't need because I implemented serde_mapping -> pydict + // I assume my impl of CheckRequest::to_pydict makes this un-neccessary + // but TBD, could be some subtle differences between it and 'load_config' // - set attr 'check_id' equal to the check id + // also parse out the attr `__version__` and record it somewhere + // ref PythonCheckLoader::Load in Agent Ok(check_handle) } + fn possible_import_paths_for_module(&self, module_name: &str) -> Vec { + vec![module_name.to_string(), format!("datadog_checks.{}", module_name)] + } + fn register_check_impl(&mut self, check: &RunnableCheckRequest) -> Result { let check_module_name = &check.check_request.name; // if there is a specific source, then this will populate into locals and can be found @@ -130,7 +139,7 @@ impl PythonCheckScheduler { return self.register_check_with_source(py_source.clone()); } let mut load_errors = vec![]; - for import_str in &[&check_module_name, &&format!("datadog_checks.{}", check_module_name)] { + for import_str in self.possible_import_paths_for_module(check_module_name).iter() { match self.register_check_from_imports(import_str) { Ok(handle) => return Ok(handle), Err(e) => { @@ -233,9 +242,20 @@ impl PythonCheckScheduler { } } impl CheckScheduler for PythonCheckScheduler { - fn can_run_check(&self, _check: &RunnableCheckRequest) -> bool { - self.register_check_from_imports(_check.check_request.name.as_str()) - .is_ok() + fn can_run_check(&self, check: &RunnableCheckRequest) -> bool { + self.possible_import_paths_for_module(check.check_request.name.as_str()) + .iter() + .map(|module_name| { + let handle = self.register_check_from_imports(module_name); + info!( + "Checking if py module '{name}' can be run. Handle exists? {}", + handle.is_ok(), + name = module_name, + ); + + handle.is_ok() + }) + .any(|x| x) } // This function does 3 things @@ -261,6 +281,7 @@ impl CheckScheduler for PythonCheckScheduler { let check_handle = check_handle.clone(); trace!("Spawning task for check instance {idx}"); + self.tlm.check_instances_started.increment(1); let handle = tokio::task::spawn(async move { let mut interval = tokio::time::interval(Duration::from_millis(instance.min_collection_interval_ms().into())); @@ -333,14 +354,14 @@ mod tests { #[tokio::test] async fn test_new_check_scheduler() { let (sender, _) = mpsc::channel(10); - let scheduler = PythonCheckScheduler::new(sender).unwrap(); + let scheduler = PythonCheckScheduler::new(sender, ChecksTelemetry::noop()).unwrap(); assert!(scheduler.running.is_empty()); } #[tokio::test] async fn test_register_check_with_source() { let (sender, _) = mpsc::channel(10); - let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender, ChecksTelemetry::noop()).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck @@ -358,7 +379,7 @@ class MyCheck(AgentCheck): #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_run_check() { let (sender, mut receiver) = mpsc::channel(10); - let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender, ChecksTelemetry::noop()).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck @@ -395,7 +416,7 @@ class MyCheck(AgentCheck): #[tokio::test] async fn test_stop_check() { let (sender, mut receiver) = mpsc::channel(10); - let mut scheduler = PythonCheckScheduler::new(sender).unwrap(); + let mut scheduler = PythonCheckScheduler::new(sender, ChecksTelemetry::noop()).unwrap(); let py_source = r#" from datadog_checks.checks import AgentCheck From dc621d2137513fe049ff7ce22759672ce9118842 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 31 May 2024 20:21:48 +0000 Subject: [PATCH 46/47] Adds links for future reference --- lib/saluki-components/src/sources/checks/listener.rs | 3 +++ lib/saluki-components/src/sources/checks/python_scheduler.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/lib/saluki-components/src/sources/checks/listener.rs b/lib/saluki-components/src/sources/checks/listener.rs index a19ffc9f..661ad873 100644 --- a/lib/saluki-components/src/sources/checks/listener.rs +++ b/lib/saluki-components/src/sources/checks/listener.rs @@ -111,6 +111,9 @@ impl DirCheckRequestListener { } } /// Retrieves all check entities from the base path that match the required check formats. + /// + /// Ref agent implementation: + /// https://github.com/DataDog/datadog-agent/blob/0eec38de4bfcd752cc10dfa700c06dd7bcdb3f36/comp/core/autodiscovery/providers/config_reader.go pub async fn get_check_entities(&self) -> Result, GenericError> { let mut sources = vec![]; for p in self.search_paths.iter() { diff --git a/lib/saluki-components/src/sources/checks/python_scheduler.rs b/lib/saluki-components/src/sources/checks/python_scheduler.rs index ba6f94d6..70dec61d 100644 --- a/lib/saluki-components/src/sources/checks/python_scheduler.rs +++ b/lib/saluki-components/src/sources/checks/python_scheduler.rs @@ -29,6 +29,8 @@ pub struct PythonCheckScheduler { impl PythonCheckScheduler { pub fn new(send_check_metrics: mpsc::Sender, tlm: ChecksTelemetry) -> Result { + // TODO future improvement to hook into py allocators to track memory usage + // ref https://docs.rs/pyembed/latest/src/pyembed/pyalloc.rs.html#605-609 pyo3::append_to_inittab!(datadog_agent); pyo3::append_to_inittab!(pyagg); From ac5f4071cf24a72dd01abf2790ff2b02bf08f3e8 Mon Sep 17 00:00:00 2001 From: Scott Opell Date: Fri, 31 May 2024 20:32:51 +0000 Subject: [PATCH 47/47] Adds notes on check dependencies --- README.md | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 869eb07a..0042615c 100644 --- a/README.md +++ b/README.md @@ -51,16 +51,38 @@ Local python checks can be placed in `./dist/foo.py` and configured via `./dist/ pyo3 is used to provide python support, which works off of your system's python install, `sudo apt install libpython3-devel` (todo double check the package name) -todo update instructions with full integrations-core package installation -``` -#pip install datadog_checks_base # maybe not needed bc its a dependency -#pip install datadog_checks_base[deps] # maybe not needed bc its a dependency -cp $HOME/dev/integrations-core/requirements-agent-release.txt . -pip install -r $(awk -v local_path_base="$HOME/dev/integrations-core" '{sub(/^datadog-/, "", $1); gsub(/-/, "_", $1); split($1, parts, "=="); package_name = parts[1]; if (index(package_name, "checks_") != 1) {local_path = local_path_base "/" package_name "/"; print local_path}}' requirements-agent-release.txt) -# One error -# ERROR: Package 'datadog-tokumx' requires a different Python: 3.10.12 not in '==2.7.*' -# Not bad. -``` +#### Getting required python libs into a venv +This is way harder than I expected. + +The required python libs are all within `integrations-core` and generally +speaking, `pip install datadog_checks_base pip install datadog_checks_base[deps]` is sufficient to +a "hello-world" style check such as what is committed in this repo. + +Once we want to run "standard" checks such as `http` or `mongodb`, what we now +want is to install all the default integrations, just like the Agent does. + + +In `integrations-core` there is a file +[requirements-agent-release.txt](https://github.com/DataDog/integrations-core/blob/master/requirements-agent-release.txt) +which is a generated `requirements.txt` that records the versions for a given +release. + +By combining this and the [wheels index]( +https://dd-integrations-core-wheels-build-stable.datadoghq.com/targets/simple/index.html) +, we _should_ be able to install the required packages. + + +Prepending `--index-url https://dd-integrations-core-wheels-build-stable.datadoghq.com/targets/simple/` +to the `requirements-agent-release.txt` and then handing it to `pip` _should_ +work. + +However it does not. The above integrations-core-wheels is backed by an s3 +bucket and does not have `index_url` set to return the `index.html` when a +directory is requested. + +Some amount of this can be hacked around with an `awk` script to process the +requirements.txt, but there are more issues that I was never fully able to +resolve. ## Contributing

jWN;FU7zXkhQ57H*RzC%wm@=U;Ybu93h5$!+MnpS4cgx_*}(9 zRGIVzM843l>Rn~26CtaJL|eKE2ev3wALZnB9$!KZb=e;FG%X3_NNIcqL&gR`Q$QsQLj)86<~3zCokHs* z01*KZKhNVV#m2DH5N8jxwbgHsGZNsmv(;gcb5VI|o!G2B2Xf>)D*Gl{nG+=^EFh}5m^WL;u%(9IELap4rsm6R1+cRdcdUTd6OM-ZUX#@K}YcxfJvrxha6de zS@yE2u7#Yck25>Y z<%aX%T=&@SMU+TqzER!zVrz)8*0huFh8SZi+sPdso{Wz%HFGkL80vbEv%+JV#WP2G z2Z3cRZ%p&-n6TC&~-03Sl-GRI-W~TYLJPdgMX1R_s%HiN;1elIxGx* zme{}PFkr|{G%~LW7;=O-!jNOcVa6XnN}MoD_e4PyR#il>T#4sT6^D?-7emffMpd!9 z!A*O+>~fgPzadwa7|y_pT`?>bE2qqe$9upiltj41Y!>e^iq9Ab_}WZQZ28c2Dv2C# z9*gHZdZbcAum#kF^)=&?`1t@)Oh)QI6y4sSKmzb%5A?u+MP@eBXG6aTl zZ&5iBq)W>Y=fj@BL*mg(+}w$(uMyx<3Gf~xo;ia6#g-&jrR@gt)EBXg#h^uqZ|!4E zg+8>0^}dJ6;^6~=*eo6%HsYyk&rxc-#HmX`YIbctu3VF=GrlZH^Qfh{PZ;hP9olRT?C5lZK8a?<^D!CGwqIO+CY0oOo%rjjqC3k$5QO#*Q zZ4aY!Q#&JZTF6D`!(wp}<7U2;J3sR`k;j>R$^7kJQ)x=wwfpgqhSAw7HPIYL1D`wU zMbm8on|s$dPC$-c7pOK1my>yjhN6+v!ffMQ^-_qDZDuFln&F<@7Z;^HjBNhAJ%!~j zv5pTpDH&9Ck*2;kk8>f1-tPF1Om!>dlp5B({C_Lv35KTT{$CUG#e6@l0{+%BIN~^sEuKq}uyq>}KId`Ha-#5~VXgVvRPye<^Q&RMxZMkj zL8NuXw|o}?a&|{8m^)KnN~F$w#c1zX47vi%=A$)zZlEMc0`(!xY_uv*?GO`j& z8t#$QLT=hrsdn{aszii6UMGGyWpoDStfUi99Z1n$322%Vk9WhoSdZZ;-8H*sQ^(If*^l3To}YxQ5O=Zqh| zC!ksr>||numwMu$(Nr^upB87hZm6uv`84HpI%^N^e0o|k+5BvmA)7$+S~p3=JqLov z)gpb9H+URTeO}wDnJ0v?Lk6g1S1%M@Lq08Z`gl$R>rhsO7$Rd@7;71+wCNmWkc9rK zMts+ql$Gj+8oiVYsCxnO1w23t^d;{bv=VS)JoQ;^mLSD^eYN-#O~@w9S*y(4TUZicBOfg59`aNHv_oW#ag8zRq`^p^cO@k|wuB^^9hT&ByFvG(S$RpiNe) z=cc<qeEM)iWYlTV(D2PvLe z8=Rzbe1j0^JpJ$1SCbE)S*L`*s+Ud2iG zbQ8us)zR(a16?mVkmns4yy#!Hv=`eYsoy3z`{)gj2t_%cZC5 z$WbgOZaQba>TX5=srzspW+tS~xY$nu(J=88lU@bbwJ)L@fA@`GO?=Nt9+p#LB)yM5}&u z%MHxvu(u(5*niQmcl3BRxf!K6=%9yxYXmehYw6+NW&sTUa%A}T89;`92^soH#3Zxb z;h)pW8y2*X6lr9}vrj3(#Fdk!lZUB9jZ)Wxq^-z~j;|Y=f4A}V-(9~TLFp=V&QaW+ zY{9FS&-2hT(W?!&o;=`}y$+PS_Eo4xZOp`x=egNYPkguKYXrx73~2$J_GYGYzQ>3O zqn(+VI6cg0WK>c)$5C+d$zZ30nH6uwz_^uwj%Go8c;Q-klTC#@zY|} zMoaA!PoYRJCu{-$uFUH2{0)TS&(u1Yv20cYsE8(KwV?p>YFV z6OBvVqNaNuN{U-5gBI~vO67IZB6ca)B6co zZxK~t#VumDm)w}^7O|UYXWvvpg~3DVj=?I?!Aln@Ya%<^;&kWj<-ga8!{1%MAVFi2 zpyZre&{&F+uGcQRfkJjZujx2F%N4XBFkX={k2N<7o|i8Sq_p) zu18b*n<+<@gaSr*StyHSkBkjuIOi=5iIfr%xxd+7*P*Ebe$TgFRGNpa>od{Yt0gj- z5#IqrR~u2Nf4G+IPc55M!qgNArF=RkD74Z*I=w@cN*Ix4ynx{^;{^FwFJSoT7cl(v3mAU>3ge$%@bKSFF1vho{(t?b;eK}drQh+h(`N+L;onZbX#J(p zE;pEYojxYspTW$)a(&F8ziu!ypFU>2G`HxW2Qym*3}%!rP5Dj%{$M6mr{clPA>n3r zFwN{eB`mYyp>#LXyCLDh46>u`8e8&z>%q+5Pro2Rbw@$Yxdn|Ag6=v(7KN;p>>Um% zSI~z3bp@@Ag4T-y;fM~AhL95yf3y5g9O)HUy=IHT?mEpbcd%%c^&x%r_Q|1UT(-zY zO1z5Al&(N03GlbaW(lZpB(oG-Zww^cV{2TW?)KOgfNYPc#Ic?-vUVh)1fwoJ>Nz?z z>e7tg3oTfYkqCKXCts{AYdJ_sb!D2lolyNLXR2;!%GnAKo^rCJVYXO64NGs(Nq|bMN?sf z7flPrL3q(rGs26eGKAHOro%%kl`NW84^~m7I{qhCQR|5=;Z;;&gjZ1|qyOY8YU0hh z?qY#n(>adMVZOlf@hVBkNoLY%A6^9RY-K0z3oio8`gHt$b`mz=KC6pWHGT_VD{t}yhCS5C}ZKhREg%d%^TQP_sJS8{TT`LK(K{Mo3g5{I{N)s-a* z0^v_z$d!^~6{m#ybs72Vk)7YK`;3bFbs4Bf#%;m~F6Pp!yidDLt6esYsy|tp*2(3X z*3-luQbS!`(7L z*C33)c*q`B#~&^R-{=e&{tO~u{0|N}qdn>0Pro4drD>G%>5f$1@t0lhpgP?v)#;3K z)oGep|F5b}4^W+!N^ut*BFipk{&jU+lv*Q7i4J}p*UK(vnN@>2t{J~qOM?MGxn-Bl zBJwPAKX>U;!0-9ibzE^s&#U9-dv#ooH}IYC8M=;(LQ}(a{4DDDIn?odB$QrQe%a;h zP#q7JO#IVR0mH9U0mH9U0mE7e>X+I|D3VdtF8)eIvuuuzo?6_Vo)GzJ3A2 z*DqlB`uU8YKl+o)E`MYFbQ>Pl?{CnCht=`3;_uXk|9}0Yy7KwT=qR$O`8%UXT=I+7 zUm9(KzkQVXnR8yK{T4=Kj21Bb_Ay}i?PI|3+ee=fw2wAIBL(7L zHR$E?zY2NZPxAkQa`}JZUzh(clmGK1f6zha|4IQD{YUeEeK`NO{E7Tu63%}~v`GHX z`upX-K2r>XE@hr7C)%Bt%-YUNWUkM+k3ntXrjU!oVvDi0bEJ1-TMvES zFbkctLyTcnj2elP!i-qt z-m#aFm?Ie}r#t@p$6n;LNA%c>oc9PHdl4KQdvT_Cc_eMT^E6rVwzteA41EmJImUCM z*Qp*gnuLky=n2fSQ`KNGbGL@TVgLNHJ`U%>AvjE?b% zb-6_j2hL8@P89?C1J3E2OdX85M9hcVsqUDkiuu`iyohu6^Tb@$$cq2O3H_CTqq@d& z;_@cOdI1J6oyZ#*TLAJ#2C^v4XXhQy zn=GJn>JXDrpPFhUpz4aW%JJpAN^lb3sDZI~{5R5ImVg34f^e4tob~vbOSo$UJYmGM zeq^lrp16j^w#he1Pjuw{^Sdk&F85VdxQAUU^1lIY#0c??nxfAzmbuK(HJ! zbHaj!5fL0Pe8D~=5bQHVuvWj7s89I~Litat?~Vks+>t=6EIuGj9RNB1*fSEmNFw$# zdy;?A`P#3j%l#!CLcrwhW6w7qp^B6I4k;KJIVTL|Xx|@`mDiX6| zmEzxFy-+|ZmJy#RwNJoeYTvWezAb>#wSB32GM(BkR@t$7dXX!G7egb9TvGRB8l@Mx z3bFMTxn3%Db=Ctd@h#^wJBjatSdKoQIRxNumQBPQoX?yA2yd22!T-;m&s->ac;_=$ zVGhn`ZUFoT&u8YOAFb3@39NdoO5)QJH%auCVU1YFL@QEP@>P#^3VFdGowp!_A^VI@ zomfL>K8HzthJKCc)`qSZapAs3l*lCvaidFpomevACq3m#q$J~VR&u9kQ7@KE{t6K1 zVq3bSMWUZ~wq9d-!OT5L*^DBcVnE{=E#7&5?dYEYzo z9d(mM;y8%`u3GKHMg5o^8I|ak@_%Yn;#Bj3E+l9j(nc@U1IB9;T;*i>x5d1OrqF zA+q`*buF@-EsS8d()qi^ZelogGyY}lBr_wi6Gjj_-JCB(h|@S$Q#a?U1cc}9k>-2@ zwr+DCmlH}`U7zmhq@4hLI?1oex@sdgsM@;nOaCIxPl_u^=tNSwacKmQ#w9MptX^W% zBxb~Ah&2(QGgC%E^E{9vL#s6&AU;6|bpXeEAyE0tYYN=R!%<<6$D z(%0GOcTNYw)o(6+KPXM#7L$8qX0IH#St!JA|6?2ATrS6mI-fq+b3Q(E@Lta4YLuvR z`ykKx@tH^LC@XE{RK%kpMo>zXS@=0BAAOvuu7QgC8P-z%UlKF- zTKsx4p9dO&d7WX(zqRxfURj2UUo}kmw{Dx_zK31>hGDInYO2c`VELv&f_%M>41bso zPu>wOzTYUg=mKsOxe)V%ZswzLkQTQ+NwGbwh|f~CGF0)Jc=m6!n7uH!d?l6nD48p< zN2+SMrM~wLVUN~&MM-5JCG%@7po^Cd zALCjNX=f8avd>$tm+M5vu6VMOhi8IiPhiUs*IwEl*%LUkiHkD@bHT(c4@-k07fcuj zxb}ke1rte$hiKVIT(r*7Q99|*MroEGC4`z!FXdgyoVk3n3M#H=aO==aOm_?7!jfxPw>>_COvw%M8QKgr>;!Godyq8kbDiECJFG{-IpRnNBWz=1QPf)c3U% z%)8vZeV}+I`u+@PJs-8?8kH%udN)JGd@R~*zLZw7Ef1~ZQ536Ch11_A>_<5#L(Q@t z1Jl%0sJLs=U#pXBD(;;hEY%5)EY)d-UaHIfp@eRPmb|!khtx({snh13spB!HxcGa` z%RS;n=73%Tmh!sxNS5`zJ4t}IoHt9%-g4ej%>VZ-=c&wbr@L*c zjDps@oTD-ucujs04Czq98KWETHg3km$}S*NBp-{%CM&(~)`oNNRD4uU(hOnQlPmD> zBi$ahc)voe4Kh%(9>HD^7~9O+76W4Q{0>UJ{SjIf11g%-Q#uRa5CT z7(xQ3Pl{*GrTq{EGH{z}ma9_lv1C0&Tk?MRD26AP6RoY9@#)X8iVmpT+DN9Z;i&gQ zKyg`H{UYxw%rYfT7-L;|lAU@Sm3;e9w>eF~DK2a6oEgIL1K6J!{ekO69K_q24NyeH zZ_wiVYw=sli)Z{(PP|06xU6p=UT{RbW@z!5+q`A@r1sM-z#_G{jH01``X&xt;?4r= zrl6E@k51j&lCL0760R+F#dpNxlBhdTQ&KovQZ+@xM2aRaz(O37Ln<}6Vdj$!L-_di zI;a}rz`^J@kdG12@Ol)3T!u4UXu+(WJkEehxat3V{)JhRGIXIRc;`m9e=lif6nwD4 z?cYmUy6tY?kk~oTqr-`G(1q0uRnk13T@6J$VV3^=Gg97H^!NMsh2ni6-iL_yR`KKv z&x_x`kNds;T?Tjsn^y8Ef}2sfy{nZ}JU_B6UZBUUcB~t^h$`t2Pf9SNjo?OU+LAf-PA1SY(Afp~5Me~g{6p5`o>TvB$>BC(5_KbTSS1CisUC){74V(GKc*APQ zm0NQER%w01x{3d7V%}(2#*3_=L#68tYn*raRw;MvJ>uQHRmvwLGQ8Wixa7dPlPhX| zgi1FXR$&869S)T?HmouHUnAz`3<&t&BIZN55s?3ahM12qtRndr8qGa}{I3-AG05aU z#=ceQiH3DvBc>!!=`U`c9qA^mRVLQ}B%kFWg~5hfI=O8kZ?6TMQ%YFfBO&E-06nLf z=nBXvlY?c+*TVv4R87FWHzg~mFFIRvpmg5Xs{9R#hZl(4iiIRcyf)(b*=__k%Ds~5x` z0oNd4s|es=tQHWYqs&P6aUpD{2w<)n6(G~$r1NqpBX+k<3@;x^{Tfe-i)Un_Z0*kF zs%NeUD81e&=)zrf=Rl>W8U?@dIlLacJ6rmrQ80jid5^Yql~HgB|C`0U%dmdn^LO$} zO6ko;L1+Hu40Y)@M!|Ic4p{O#y7e3t#meZ;vhOiyUk1P4N;}t8S(uUz@2gB zRw=af-5#X}GH%w*l1@#kyYiboxme^6T_%$ILK9nGPg2@Jgg>~HRzXt1igj>@1*O+X zXJbBB+4~quL4ztWeFfpk)r|tPYD{0KcXDmFrpb_qB4(A{hCazPUIurEdvc9iV zX94aO&y)=`!OhUa-9*#L9jGP#_ra6(w7jSRoqVj}&YYjT+*5IcQP5<*r{WRiQtlIx z{#{tb!^){R>R+oU{-Y}Xt|w(?G~`~?6px|M6x&_V=t{)>f@K`crWG_Lp84HbZo0_0 zbxNX&s#)5XVNuBKVYpi;ZjaR?oy)MIFc@Vk;C&_h6S6DO2<5XFH09L#6e@kw?7H!& z@HCH(Hz;*R9b=!&UFW%1%$`vt;+~1-eMozWNB;eMVMyj(?j5f4YcNN=}mj zIsbB#=Imm_s$bL6#d!8O!#WPLTnjn7#ISC~EM;o;B*Xd)vy`dXCs9r0e$RrIwcM4j z*)OxDUl-sAim(R42x+MrQ`noN&y4AuN-O=co zTvaZozyH0QexKoe?tQ-GH_J=@NhH?~OMc7fo?Kf_@^8Nh$#zXQ{` z*=vl%_HYScDZq&ofd{!x^s(|8`oE?OaGvcZN`iEQR=?{kC1`PmVNF}Yr?u8&W|$f_ z&wk%hsU3z|OkJJ$fu(jri>tE&)C>>DL5oLHj9Rb;Y0c|ttLLHn9mD#*7vmA~Nz8}y zrRS?4%!DjX&GzEH50AOfm^?<$tXYnP#uORWT$U-fK*Ilm|1OvA%@4WJ>rcxl-ZaIr zvXfh-si&YZoiZ2Ia3@7FRUXsXNxnt}mu^q)ZyVF4YT|g$UYdK|5Pax4sO&Qud37yd z_;oD<7m)>OVfYKw$?yBQ0p-FR)7^0H_tqnWD(fGp3K;&@Wx((cR0Rw_WMNdify1G$ zn-5fqvoLOI^k|)?u7F656~uNec3x_vCOySeZil2qj$WyAXePz3`0KiQrOwB}N?pLv zD|M;MS!-K}|KhLri=U5q6=uB%X8|?{@E75>3h)-;c8b|sgiHN|y8hwFJc6fE250JI zpN&`qw-EYx&n<*aagie~yjvB0>>r;Duy=e?utYF?mDJtvBy;zisp}0#RyO=Ibv_Qx z)G=VAm>h?*syOq@k>E%D2y3zn3l2t#u73|vIYkR*=Gdh$Y=N=6ER(@uE9X9vdiA347F{e%hA36D{Gas8`rz35i1))O5EtC zhJ5B-?4)4P#smwUtWM~f^}nLcvFg;f)p3xt*jxB-$dTzWIfnHv|1BC~rd9u9f4%|G z+jY~A|Et7|n>+abRm>dt)_s#6fgDTv`6KiV?=#w5;PvV4uvpZ#a`Gh)%WmxAs>#ez z4DTc(q|IGi%n6$0In!N^)aFI)bDdq&_TbK+SQ&}S&I~b*)7Sa74UNebb%qWm;N zU!uCAk(<2|51b51ynnFEa9oD;9b}x0gbI>3xn}~>xn0hD{%TX@zRV+QA=$*|TkLaJ znd$^6y%V6py{4)RrL$;~`}%5A)+SYY8a5~LO%|Qad4{1cgqNb{m*P`~rm6d&^h|e* zeNxEot`C`K%H@nh*scjKXPgKKE@zw}!0%BP3h;W=Rbuvf)D4(}9#vAK1A+YQPpP|u z`9Y!dc#3qghmlq}hBIxto3XvbzOs?)TpD7G?!_+3d0uIhj2_)AmU8^!D$6jV*=GsL zt1u$vRT$y&O1q)UYjCx%W`u94(8W})_VsV6kRmDNmcReOV(Q*8A14sMJf%pV!IDN? z{i!!|^^(L!TEknDwPGBvh*`myDYzh5Ui(6Dw zRFIgciF-gQagVJi*4m^lE$*>aje8VZajB+ti;9X?E!GWp)MEeN@0ok&-HD*M6!8CR zKA(BH=gghEob8^w3t!Id$(LD1l8P@-oeO%Xj!4EoQ4o=gC-+I>66ioOZc#aC$#~4j zk&G0lxbm#(5LQ>m9LPEqBV=ey-#ftUU0xK?G87p!FTWPn(CMK=z@lZ*- zd$8g|gY;y@8wM<__+w92Jh&28+yGWAQLNaa2P^LH$%-d?vSRVqS+O$`Sst(=vmRKH z*(^}f}OLEITOi z68d|d5}J(xtZDsSuri^+P%ZV>Y29oxW*P8MX#S&wA`hjcW$$-lppDcW3XN~TA~cUH zo-=}2_P4&qVk>hWgk~v-PW|1-6q