diff --git a/src/bin/inx-chronicle/cli.rs b/src/bin/inx-chronicle/cli.rs index 3ce00f37f..3ece00541 100644 --- a/src/bin/inx-chronicle/cli.rs +++ b/src/bin/inx-chronicle/cli.rs @@ -177,7 +177,7 @@ impl ClArgs { /// Process subcommands and return whether the app should early exit. #[allow(unused)] #[allow(clippy::collapsible_match)] - pub async fn process_subcommands(&self, config: &ChronicleConfig) -> Result { + pub async fn process_subcommands(&self, config: &ChronicleConfig) -> Result { if let Some(subcommand) = &self.subcommand { match subcommand { #[cfg(feature = "api")] @@ -195,13 +195,13 @@ impl ClArgs { let exp_ts = time::OffsetDateTime::from_unix_timestamp(claims.exp.unwrap() as _).unwrap(); let jwt = auth_helper::jwt::JsonWebToken::new(claims, api_data.secret_key.as_ref()) .map_err(crate::api::ApiError::InvalidJwt)?; - println!("Bearer {}", jwt); - println!( + tracing::info!("Bearer {}", jwt); + tracing::info!( "Expires: {} ({})", exp_ts, humantime::format_duration(api_data.jwt_expiration) ); - return Ok(true); + return Ok(PostCommand::Exit); } #[cfg(feature = "analytics")] Subcommands::FillAnalytics { @@ -209,6 +209,7 @@ impl ClArgs { end_milestone, num_tasks, } => { + tracing::info!("Connecting to database using hosts: `{}`.", config.mongodb.hosts_str()?); let db = chronicle::db::MongoDb::connect(&config.mongodb).await?; let start_milestone = if let Some(index) = start_milestone { *index @@ -244,9 +245,9 @@ impl ClArgs { { let analytics = db.get_all_analytics(index).await?; influx_db.insert_all_analytics(timestamp, index, analytics).await?; - println!("Finished analytics for milestone: {}", index); + tracing::info!("Finished analytics for milestone {}", index); } else { - println!("No milestone in database for index {}", index); + tracing::info!("No milestone in database for index {}", index); } } Result::<_, Error>::Ok(()) @@ -256,12 +257,29 @@ impl ClArgs { // Panic: Acceptable risk res.unwrap()?; } - return Ok(true); + return Ok(PostCommand::Exit); + } + #[cfg(debug_assertions)] + Subcommands::ClearDatabase { run } => { + tracing::info!("Connecting to database using hosts: `{}`.", config.mongodb.hosts_str()?); + let db = chronicle::db::MongoDb::connect(&config.mongodb).await?; + db.clear().await?; + tracing::info!("Database cleared successfully."); + if !run { + return Ok(PostCommand::Exit); + } + } + Subcommands::BuildIndexes => { + tracing::info!("Connecting to database using hosts: `{}`.", config.mongodb.hosts_str()?); + let db = chronicle::db::MongoDb::connect(&config.mongodb).await?; + super::build_indexes(&db).await?; + tracing::info!("Indexes built successfully."); + return Ok(PostCommand::Exit); } _ => (), } } - Ok(false) + Ok(PostCommand::Start) } } @@ -270,13 +288,32 @@ pub enum Subcommands { /// Generate a JWT token using the available config. #[cfg(feature = "api")] GenerateJWT, + /// Manually fill the analytics database. #[cfg(feature = "analytics")] FillAnalytics { + /// The inclusive starting milestone index. #[arg(short, long)] start_milestone: Option, + /// The exclusive ending milestone index. #[arg(short, long)] end_milestone: Option, + /// The number of parallel tasks to use when filling the analytics. #[arg(short, long)] num_tasks: Option, }, + /// Clear the chronicle database. + #[cfg(debug_assertions)] + ClearDatabase { + /// Run the application after this command. + #[arg(short, long)] + run: bool, + }, + /// Manually build indexes. + BuildIndexes, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PostCommand { + Start, + Exit, } diff --git a/src/bin/inx-chronicle/main.rs b/src/bin/inx-chronicle/main.rs index ef975e35b..9f760cfd1 100644 --- a/src/bin/inx-chronicle/main.rs +++ b/src/bin/inx-chronicle/main.rs @@ -21,7 +21,10 @@ use tokio::task::JoinSet; use tracing::{debug, error, info}; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; -use crate::{cli::ClArgs, error::Error}; +use self::{ + cli::{ClArgs, PostCommand}, + error::Error, +}; #[tokio::main] async fn main() -> Result<(), Error> { @@ -33,13 +36,13 @@ async fn main() -> Result<(), Error> { let cl_args = ClArgs::parse(); let config = cl_args.get_config()?; - if cl_args.process_subcommands(&config).await? { + if cl_args.process_subcommands(&config).await? == PostCommand::Exit { return Ok(()); } set_up_logging(&config)?; - info!("Connecting to database at bind address `{}`.", config.mongodb.conn_str); + info!("Connecting to database using hosts: `{}`.", config.mongodb.hosts_str()?); let db = MongoDb::connect(&config.mongodb).await?; debug!("Available databases: `{:?}`", db.get_databases().await?); info!( @@ -49,30 +52,7 @@ async fn main() -> Result<(), Error> { ); #[cfg(feature = "stardust")] - { - use chronicle::db::collections; - let start_indexes = db.get_index_names().await?; - db.create_indexes::().await?; - db.create_indexes::().await?; - db.create_indexes::().await?; - db.create_indexes::().await?; - let end_indexes = db.get_index_names().await?; - for (collection, indexes) in end_indexes { - if let Some(old_indexes) = start_indexes.get(&collection) { - let num_created = indexes.difference(old_indexes).count(); - if num_created > 0 { - info!("Created {} new indexes in {}", num_created, collection); - if tracing::enabled!(tracing::Level::DEBUG) { - for index in indexes.difference(old_indexes) { - debug!(" - {}", index); - } - } - } - } else { - info!("Created {} new indexes in {}", indexes.len(), collection); - } - } - } + build_indexes(&db).await?; let mut tasks: JoinSet> = JoinSet::new(); @@ -186,3 +166,30 @@ fn set_up_logging(#[allow(unused)] config: &ChronicleConfig) -> Result<(), Error registry.init(); Ok(()) } + +#[cfg(feature = "stardust")] +async fn build_indexes(db: &MongoDb) -> Result<(), Error> { + use chronicle::db::collections; + let start_indexes = db.get_index_names().await?; + db.create_indexes::().await?; + db.create_indexes::().await?; + db.create_indexes::().await?; + db.create_indexes::().await?; + let end_indexes = db.get_index_names().await?; + for (collection, indexes) in end_indexes { + if let Some(old_indexes) = start_indexes.get(&collection) { + let num_created = indexes.difference(old_indexes).count(); + if num_created > 0 { + info!("Created {} new indexes in {}", num_created, collection); + if tracing::enabled!(tracing::Level::DEBUG) { + for index in indexes.difference(old_indexes) { + debug!(" - {}", index); + } + } + } + } else { + info!("Created {} new indexes in {}", indexes.len(), collection); + } + } + Ok(()) +} diff --git a/src/db/mongodb/mod.rs b/src/db/mongodb/mod.rs index 99133ce91..52f4b13c1 100644 --- a/src/db/mongodb/mod.rs +++ b/src/db/mongodb/mod.rs @@ -10,7 +10,7 @@ use std::collections::{HashMap, HashSet}; use mongodb::{ bson::{doc, Document}, error::Error, - options::{ClientOptions, Credential}, + options::{ClientOptions, ConnectionString, Credential, HostInfo}, Client, }; use serde::{Deserialize, Serialize}; @@ -28,7 +28,7 @@ pub struct MongoDb { impl MongoDb { const DEFAULT_NAME: &'static str = "chronicle"; - const DEFAULT_CONNECT_URL: &'static str = "mongodb://localhost:27017"; + const DEFAULT_CONNECT_STR: &'static str = "mongodb://localhost:27017"; /// Constructs a [`MongoDb`] by connecting to a MongoDB instance. pub async fn connect(config: &MongoDbConfig) -> Result { @@ -149,10 +149,22 @@ pub struct MongoDbConfig { pub min_pool_size: Option, } +impl MongoDbConfig { + /// Get the hosts portion of the connection string. + pub fn hosts_str(&self) -> Result { + let hosts = ConnectionString::parse(&self.conn_str)?.host_info; + Ok(match hosts { + HostInfo::HostIdentifiers(hosts) => hosts.iter().map(ToString::to_string).collect::>().join(","), + HostInfo::DnsRecord(hostname) => hostname, + _ => unreachable!(), + }) + } +} + impl Default for MongoDbConfig { fn default() -> Self { Self { - conn_str: MongoDb::DEFAULT_CONNECT_URL.to_string(), + conn_str: MongoDb::DEFAULT_CONNECT_STR.to_string(), username: None, password: None, database_name: MongoDb::DEFAULT_NAME.to_string(),