1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-06-27 16:35:59 +00:00

Merge branch 'refactor-main' into 'next'

return ExitCode instead of using panic or exit

See merge request famedly/conduit!639
This commit is contained in:
Charles Hall 2024-06-11 07:48:22 +00:00
commit 75a8d89cfa
3 changed files with 98 additions and 48 deletions

64
src/error.rs Normal file
View file

@ -0,0 +1,64 @@
//! Error handling facilities
use std::{fmt, iter};
use thiserror::Error;
/// Wraps any [`Error`][e] type so that [`Display`][d] includes its [sources][s]
///
/// [e]: std::error::Error
/// [d]: fmt::Display
/// [s]: std::error::Error::source
pub struct Chain<'a>(pub &'a dyn std::error::Error);
impl fmt::Display for Chain<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)?;
let mut source = self.0.source();
source
.into_iter()
.chain(iter::from_fn(|| {
source = source.and_then(std::error::Error::source);
source
}))
.try_for_each(|source| write!(f, ": {source}"))
}
}
/// Top-level errors
// Missing docs are allowed here since that kind of information should be
// encoded in the error messages themselves anyway.
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum CliError {
#[error(
"the `CONDUIT_CONFIG` environment variable must either be set to a configuration file path \
or set to an empty string to force configuration through environment variables"
)]
ConfigPathUnset,
#[error("invalid configuration")]
ConfigInvalid(#[from] figment::Error),
// Upstream's documentation on what this error means is very sparse
#[error("opentelemetry error")]
Otel(#[from] opentelemetry::trace::TraceError),
#[error("invalid log filter syntax")]
EnvFilter(#[from] tracing_subscriber::filter::ParseError),
#[error("failed to install global default tracing subscriber")]
SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError),
// Upstream's documentation on what this error means is very sparse
#[error("tracing_flame error")]
TracingFlame(#[from] tracing_flame::Error),
#[error("failed to load or create the database")]
DatabaseError(#[source] crate::utils::error::Error),
#[error("failed to serve requests")]
Serve(#[source] std::io::Error),
}

View file

@ -2,6 +2,7 @@ pub mod api;
pub mod clap; pub mod clap;
mod config; mod config;
mod database; mod database;
pub mod error;
mod service; mod service;
mod utils; mod utils;

View file

@ -1,4 +1,4 @@
use std::{future::Future, io, net::SocketAddr, sync::atomic, time::Duration}; use std::{future::Future, io, net::SocketAddr, process::ExitCode, sync::atomic, time::Duration};
use axum::{ use axum::{
extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, extract::{DefaultBodyLimit, FromRequestParts, MatchedPath},
@ -31,7 +31,7 @@ use tower_http::{
trace::TraceLayer, trace::TraceLayer,
ServiceBuilderExt as _, ServiceBuilderExt as _,
}; };
use tracing::{debug, error, info, warn}; use tracing::{debug, info, warn};
use tracing_subscriber::{prelude::*, EnvFilter}; use tracing_subscriber::{prelude::*, EnvFilter};
pub use conduit::*; // Re-export everything from the library crate pub use conduit::*; // Re-export everything from the library crate
@ -44,27 +44,28 @@ use tikv_jemallocator::Jemalloc;
static GLOBAL: Jemalloc = Jemalloc; static GLOBAL: Jemalloc = Jemalloc;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> ExitCode {
let Err(e) = try_main().await else {
return ExitCode::SUCCESS;
};
eprintln!("error: {}", error::Chain(&e));
ExitCode::FAILURE
}
/// Fallible entrypoint
async fn try_main() -> Result<(), error::CliError> {
use error::CliError as Error;
clap::parse(); clap::parse();
// Initialize config // Initialize config
let raw_config = let raw_config = Figment::new()
Figment::new() .merge(Toml::file(Env::var("CONDUIT_CONFIG").ok_or(Error::ConfigPathUnset)?).nested())
.merge( .merge(Env::prefixed("CONDUIT_").global());
Toml::file(Env::var("CONDUIT_CONFIG").expect(
"The CONDUIT_CONFIG env var needs to be set. Example: /etc/conduit.toml",
))
.nested(),
)
.merge(Env::prefixed("CONDUIT_").global());
let config = match raw_config.extract::<Config>() { let config = raw_config.extract::<Config>()?;
Ok(s) => s,
Err(e) => {
eprintln!("It looks like your config is invalid. The following error occurred: {e}");
std::process::exit(1);
}
};
config.warn_deprecated(); config.warn_deprecated();
@ -73,47 +74,31 @@ async fn main() {
let tracer = opentelemetry_jaeger::new_agent_pipeline() let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_auto_split_batch(true) .with_auto_split_batch(true)
.with_service_name("conduit") .with_service_name("conduit")
.install_batch(opentelemetry::runtime::Tokio) .install_batch(opentelemetry::runtime::Tokio)?;
.unwrap();
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
let filter_layer = match EnvFilter::try_new(&config.log) { let filter_layer = EnvFilter::try_new(&config.log)?;
Ok(s) => s,
Err(e) => {
eprintln!(
"It looks like your log config is invalid. The following error occurred: {e}"
);
EnvFilter::try_new("warn").unwrap()
}
};
let subscriber = tracing_subscriber::Registry::default() let subscriber = tracing_subscriber::Registry::default()
.with(filter_layer) .with(filter_layer)
.with(telemetry); .with(telemetry);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} else if config.tracing_flame { } else if config.tracing_flame {
let registry = tracing_subscriber::Registry::default(); let registry = tracing_subscriber::Registry::default();
let (flame_layer, _guard) = let (flame_layer, _guard) = tracing_flame::FlameLayer::with_file("./tracing.folded")?;
tracing_flame::FlameLayer::with_file("./tracing.folded").unwrap();
let flame_layer = flame_layer.with_empty_samples(false); let flame_layer = flame_layer.with_empty_samples(false);
let filter_layer = EnvFilter::new("trace,h2=off"); let filter_layer = EnvFilter::new("trace,h2=off");
let subscriber = registry.with(filter_layer).with(flame_layer); let subscriber = registry.with(filter_layer).with(flame_layer);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} else { } else {
let registry = tracing_subscriber::Registry::default(); let registry = tracing_subscriber::Registry::default();
let fmt_layer = tracing_subscriber::fmt::Layer::new(); let fmt_layer = tracing_subscriber::fmt::Layer::new();
let filter_layer = match EnvFilter::try_new(&config.log) { let filter_layer = EnvFilter::try_new(&config.log)?;
Ok(s) => s,
Err(e) => {
eprintln!("It looks like your config is invalid. The following error occured while parsing it: {e}");
EnvFilter::try_new("warn").unwrap()
}
};
let subscriber = registry.with(filter_layer).with(fmt_layer); let subscriber = registry.with(filter_layer).with(fmt_layer);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} }
// This is needed for opening lots of file descriptors, which tends to // This is needed for opening lots of file descriptors, which tends to
@ -127,19 +112,19 @@ async fn main() {
maximize_fd_limit().expect("should be able to increase the soft limit to the hard limit"); maximize_fd_limit().expect("should be able to increase the soft limit to the hard limit");
info!("Loading database"); info!("Loading database");
if let Err(error) = KeyValueDatabase::load_or_create(config).await { KeyValueDatabase::load_or_create(config)
error!(?error, "The database couldn't be loaded or created"); .await
.map_err(Error::DatabaseError)?;
std::process::exit(1);
};
let config = &services().globals.config; let config = &services().globals.config;
info!("Starting server"); info!("Starting server");
run_server().await.unwrap(); run_server().await.map_err(Error::Serve)?;
if config.allow_jaeger { if config.allow_jaeger {
opentelemetry::global::shutdown_tracer_provider(); opentelemetry::global::shutdown_tracer_provider();
} }
Ok(())
} }
/// Adds additional headers to prevent any potential XSS attacks via the media repo /// Adds additional headers to prevent any potential XSS attacks via the media repo