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:
commit
75a8d89cfa
3 changed files with 98 additions and 48 deletions
64
src/error.rs
Normal file
64
src/error.rs
Normal 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),
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
81
src/main.rs
81
src/main.rs
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue