2019-08-29 14:46:54 -07:00
|
|
|
//! Zebrad Abscissa Application
|
|
|
|
|
|
|
|
use crate::{commands::ZebradCmd, config::ZebradConfig};
|
|
|
|
use abscissa_core::{
|
2019-12-20 11:20:04 -08:00
|
|
|
application::{self, AppCell},
|
2020-06-04 19:34:06 -07:00
|
|
|
config,
|
|
|
|
terminal::component::Terminal,
|
|
|
|
trace::Tracing,
|
|
|
|
Application, Component, EntryPoint, FrameworkError, StandardPaths,
|
2019-08-29 14:46:54 -07:00
|
|
|
};
|
|
|
|
|
2019-12-20 11:20:04 -08:00
|
|
|
/// Application state
|
|
|
|
pub static APPLICATION: AppCell<ZebradApp> = AppCell::new();
|
2019-08-29 14:46:54 -07:00
|
|
|
|
|
|
|
/// Obtain a read-only (multi-reader) lock on the application state.
|
|
|
|
///
|
|
|
|
/// Panics if the application state has not been initialized.
|
|
|
|
pub fn app_reader() -> application::lock::Reader<ZebradApp> {
|
|
|
|
APPLICATION.read()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Obtain an exclusive mutable lock on the application state.
|
|
|
|
pub fn app_writer() -> application::lock::Writer<ZebradApp> {
|
|
|
|
APPLICATION.write()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Obtain a read-only (multi-reader) lock on the application configuration.
|
|
|
|
///
|
|
|
|
/// Panics if the application configuration has not been loaded.
|
|
|
|
pub fn app_config() -> config::Reader<ZebradApp> {
|
|
|
|
config::Reader::new(&APPLICATION)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Zebrad Application
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ZebradApp {
|
|
|
|
/// Application configuration.
|
|
|
|
config: Option<ZebradConfig>,
|
|
|
|
|
|
|
|
/// Application state.
|
|
|
|
state: application::State<Self>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initialize a new application instance.
|
|
|
|
///
|
|
|
|
/// By default no configuration is loaded, and the framework state is
|
|
|
|
/// initialized to a default, empty state (no components, threads, etc).
|
|
|
|
impl Default for ZebradApp {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
config: None,
|
|
|
|
state: application::State::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Application for ZebradApp {
|
|
|
|
/// Entrypoint command for this application.
|
|
|
|
type Cmd = EntryPoint<ZebradCmd>;
|
|
|
|
|
|
|
|
/// Application configuration.
|
|
|
|
type Cfg = ZebradConfig;
|
|
|
|
|
|
|
|
/// Paths to resources within the application.
|
|
|
|
type Paths = StandardPaths;
|
|
|
|
|
|
|
|
/// Accessor for application configuration.
|
|
|
|
fn config(&self) -> &ZebradConfig {
|
|
|
|
self.config.as_ref().expect("config not loaded")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Borrow the application state immutably.
|
|
|
|
fn state(&self) -> &application::State<Self> {
|
|
|
|
&self.state
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Borrow the application state mutably.
|
|
|
|
fn state_mut(&mut self) -> &mut application::State<Self> {
|
|
|
|
&mut self.state
|
|
|
|
}
|
|
|
|
|
2020-06-04 19:34:06 -07:00
|
|
|
fn framework_components(
|
|
|
|
&mut self,
|
|
|
|
command: &Self::Cmd,
|
|
|
|
) -> Result<Vec<Box<dyn Component<Self>>>, FrameworkError> {
|
|
|
|
let terminal = Terminal::new(self.term_colors(command));
|
|
|
|
let tracing = self.tracing_component(command);
|
2020-06-22 15:36:23 -07:00
|
|
|
color_eyre::install().unwrap();
|
2020-06-04 19:34:06 -07:00
|
|
|
|
|
|
|
Ok(vec![Box::new(terminal), Box::new(tracing)])
|
|
|
|
}
|
|
|
|
|
2019-08-29 14:46:54 -07:00
|
|
|
/// Register all components used by this application.
|
|
|
|
///
|
|
|
|
/// If you would like to add additional components to your application
|
|
|
|
/// beyond the default ones provided by the framework, this is the place
|
|
|
|
/// to do so.
|
|
|
|
fn register_components(&mut self, command: &Self::Cmd) -> Result<(), FrameworkError> {
|
2020-02-14 13:38:33 -08:00
|
|
|
use crate::components::{
|
|
|
|
metrics::MetricsEndpoint, tokio::TokioComponent, tracing::TracingEndpoint,
|
|
|
|
};
|
2019-09-09 13:05:42 -07:00
|
|
|
|
|
|
|
let mut components = self.framework_components(command)?;
|
|
|
|
components.push(Box::new(TokioComponent::new()?));
|
|
|
|
components.push(Box::new(TracingEndpoint::new()?));
|
2020-02-14 13:38:33 -08:00
|
|
|
components.push(Box::new(MetricsEndpoint::new()?));
|
2019-09-09 13:05:42 -07:00
|
|
|
|
2019-08-29 14:46:54 -07:00
|
|
|
self.state.components.register(components)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Post-configuration lifecycle callback.
|
|
|
|
///
|
|
|
|
/// Called regardless of whether config is loaded to indicate this is the
|
|
|
|
/// time in app lifecycle when configuration would be loaded if
|
|
|
|
/// possible.
|
2020-06-04 19:34:06 -07:00
|
|
|
fn after_config(
|
|
|
|
&mut self,
|
|
|
|
config: Self::Cfg,
|
|
|
|
command: &Self::Cmd,
|
|
|
|
) -> Result<(), FrameworkError> {
|
2019-08-29 14:46:54 -07:00
|
|
|
// Configure components
|
|
|
|
self.state.components.after_config(&config)?;
|
|
|
|
self.config = Some(config);
|
2020-06-04 19:34:06 -07:00
|
|
|
|
|
|
|
let level = self.level(command);
|
|
|
|
self.state
|
|
|
|
.components
|
|
|
|
.get_downcast_mut::<Tracing>()
|
|
|
|
.expect("Tracing component should be available")
|
|
|
|
.reload_filter(level);
|
|
|
|
|
2019-08-29 14:46:54 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-06-04 19:34:06 -07:00
|
|
|
}
|
2019-08-29 14:46:54 -07:00
|
|
|
|
2020-06-04 19:34:06 -07:00
|
|
|
impl ZebradApp {
|
|
|
|
fn level(&self, command: &EntryPoint<ZebradCmd>) -> String {
|
|
|
|
if let Ok(level) = std::env::var("ZEBRAD_LOG") {
|
|
|
|
level
|
fix panic in seed subcommand (#401)
Co-authored-by: Jane Lusby <jane@zfnd.org>
Prior to this change, the seed subcommand would consistently encounter a panic in one of the background tasks, but would continue running after the panic. This is indicative of two bugs.
First, zebrad was not configured to treat panics as non recoverable and instead defaulted to the tokio defaults, which are to catch panics in tasks and return them via the join handle if available, or to print them if the join handle has been discarded. This is likely a poor fit for zebrad as an application, we do not need to maximize uptime or minimize the extent of an outage should one of our tasks / services start encountering panics. Ignoring a panic increases our risk of observing invalid state, causing all sorts of wild and bad bugs. To deal with this we've switched the default panic behavior from `unwind` to `abort`. This makes panics fail immediately and take down the entire application, regardless of where they occur, which is consistent with our treatment of misbehaving connections.
The second bug is the panic itself. This was triggered by a duplicate entry in the initial_peers set. To fix this we've switched the storage for the peers from a `Vec` to a `HashSet`, which has similar properties but guarantees uniqueness of its keys.
2020-05-27 17:40:12 -07:00
|
|
|
} else if command.verbose {
|
2020-06-04 19:34:06 -07:00
|
|
|
"debug".to_string()
|
|
|
|
} else if let Some(ZebradConfig {
|
|
|
|
tracing:
|
|
|
|
crate::config::TracingSection {
|
|
|
|
filter: Some(filter),
|
|
|
|
},
|
|
|
|
..
|
|
|
|
}) = &self.config
|
|
|
|
{
|
|
|
|
filter.clone()
|
2019-08-29 14:46:54 -07:00
|
|
|
} else {
|
2020-06-04 19:34:06 -07:00
|
|
|
"info".to_string()
|
2019-08-29 14:46:54 -07:00
|
|
|
}
|
|
|
|
}
|
2020-06-04 19:34:06 -07:00
|
|
|
|
|
|
|
fn tracing_component(&self, command: &EntryPoint<ZebradCmd>) -> Tracing {
|
|
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
|
|
|
|
|
|
// Construct a tracing subscriber with the supplied filter and enable reloading.
|
|
|
|
let builder = tracing_subscriber::FmtSubscriber::builder()
|
|
|
|
.with_env_filter(self.level(command))
|
|
|
|
.with_filter_reloading();
|
|
|
|
let filter_handle = builder.reload_handle();
|
|
|
|
|
|
|
|
builder
|
|
|
|
.finish()
|
|
|
|
.with(tracing_error::ErrorLayer::default())
|
|
|
|
.init();
|
|
|
|
|
|
|
|
filter_handle.into()
|
|
|
|
}
|
2019-08-29 14:46:54 -07:00
|
|
|
}
|