adds start as default subcommand for zebrad (#4957)
* adds start as default subcommand for zebrad
* moves EntryPoint to submodule and adds a test
* moves all start tests to config_test to avoid listener conflicts
* Update zebrad/src/application/entry_point.rs docs
* Revert "moves all start tests to config_test to avoid listener conflicts"
This reverts commit 61ce46f5a1
.
* Update based on test API changes from another PR
Co-authored-by: teor <teor@riseup.net>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
ecf2d80175
commit
3ff56c22cd
|
@ -76,7 +76,7 @@ zebra-rpc = { path = "../zebra-rpc" }
|
|||
zebra-state = { path = "../zebra-state" }
|
||||
|
||||
abscissa_core = "0.5"
|
||||
gumdrop = "0.7"
|
||||
gumdrop = { version = "0.7", features = ["default_expr"]}
|
||||
chrono = { version = "0.4.20", default-features = false, features = ["clock", "std"] }
|
||||
humantime = "2.1.0"
|
||||
humantime-serde = "1.1.1"
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
//! Zebrad Abscissa Application
|
||||
|
||||
mod entry_point;
|
||||
use self::entry_point::EntryPoint;
|
||||
|
||||
use std::{fmt::Write as _, io::Write as _, process};
|
||||
|
||||
use abscissa_core::{
|
||||
|
@ -7,7 +10,7 @@ use abscissa_core::{
|
|||
config::{self, Configurable},
|
||||
status_err,
|
||||
terminal::{component::Terminal, stderr, stdout, ColorChoice},
|
||||
Application, Component, EntryPoint, FrameworkError, Shutdown, StandardPaths, Version,
|
||||
Application, Component, FrameworkError, Shutdown, StandardPaths, Version,
|
||||
};
|
||||
|
||||
use zebra_network::constants::PORT_IN_USE_ERROR;
|
||||
|
@ -138,7 +141,7 @@ impl Default for ZebradApp {
|
|||
|
||||
impl Application for ZebradApp {
|
||||
/// Entrypoint command for this application.
|
||||
type Cmd = EntryPoint<ZebradCmd>;
|
||||
type Cmd = EntryPoint;
|
||||
|
||||
/// Application configuration.
|
||||
type Cfg = ZebradConfig;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
//! Zebrad EntryPoint
|
||||
|
||||
use crate::{
|
||||
commands::{StartCmd, ZebradCmd},
|
||||
config::ZebradConfig,
|
||||
};
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use abscissa_core::{
|
||||
command::{Command, Usage},
|
||||
config::Configurable,
|
||||
FrameworkError, Options, Runnable,
|
||||
};
|
||||
|
||||
// (See https://docs.rs/abscissa_core/0.5.2/src/abscissa_core/command/entrypoint.rs.html)
|
||||
/// Toplevel entrypoint command.
|
||||
///
|
||||
/// Handles obtaining toplevel help as well as verbosity settings.
|
||||
#[derive(Debug, Options)]
|
||||
pub struct EntryPoint {
|
||||
/// Path to the configuration file
|
||||
#[options(short = "c", help = "path to configuration file")]
|
||||
pub config: Option<PathBuf>,
|
||||
|
||||
/// Obtain help about the current command
|
||||
#[options(short = "h", help = "print help message")]
|
||||
pub help: bool,
|
||||
|
||||
/// Increase verbosity setting
|
||||
#[options(short = "v", help = "be verbose")]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Subcommand to execute.
|
||||
///
|
||||
/// The `command` option will delegate option parsing to the command type,
|
||||
/// starting at the first free argument. Defaults to start.
|
||||
#[options(command, default_expr = "Some(ZebradCmd::Start(StartCmd::default()))")]
|
||||
pub command: Option<ZebradCmd>,
|
||||
}
|
||||
|
||||
impl EntryPoint {
|
||||
/// Borrow the underlying command type
|
||||
fn command(&self) -> &ZebradCmd {
|
||||
self.command
|
||||
.as_ref()
|
||||
.expect("Some(ZebradCmd::Start(StartCmd::default()) as default value")
|
||||
}
|
||||
}
|
||||
|
||||
impl Runnable for EntryPoint {
|
||||
fn run(&self) {
|
||||
self.command().run()
|
||||
}
|
||||
}
|
||||
|
||||
impl Command for EntryPoint {
|
||||
/// Name of this program as a string
|
||||
fn name() -> &'static str {
|
||||
ZebradCmd::name()
|
||||
}
|
||||
|
||||
/// Description of this program
|
||||
fn description() -> &'static str {
|
||||
ZebradCmd::description()
|
||||
}
|
||||
|
||||
/// Version of this program
|
||||
fn version() -> &'static str {
|
||||
ZebradCmd::version()
|
||||
}
|
||||
|
||||
/// Authors of this program
|
||||
fn authors() -> &'static str {
|
||||
ZebradCmd::authors()
|
||||
}
|
||||
|
||||
/// Get usage information for a particular subcommand (if available)
|
||||
fn subcommand_usage(command: &str) -> Option<Usage> {
|
||||
ZebradCmd::subcommand_usage(command)
|
||||
}
|
||||
}
|
||||
|
||||
impl Configurable<ZebradConfig> for EntryPoint {
|
||||
/// Path to the command's configuration file
|
||||
fn config_path(&self) -> Option<PathBuf> {
|
||||
match &self.config {
|
||||
// Use explicit `-c`/`--config` argument if passed
|
||||
Some(cfg) => Some(cfg.clone()),
|
||||
|
||||
// Otherwise defer to the toplevel command's config path logic
|
||||
None => self.command.as_ref().and_then(|cmd| cmd.config_path()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the configuration after it has been loaded, potentially
|
||||
/// modifying it or returning an error if options are incompatible
|
||||
fn process_config(&self, config: ZebradConfig) -> Result<ZebradConfig, FrameworkError> {
|
||||
match &self.command {
|
||||
Some(cmd) => cmd.process_config(config),
|
||||
None => Ok(config),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,10 +9,12 @@ mod version;
|
|||
|
||||
use self::ZebradCmd::*;
|
||||
use self::{
|
||||
copy_state::CopyStateCmd, download::DownloadCmd, generate::GenerateCmd, start::StartCmd,
|
||||
copy_state::CopyStateCmd, download::DownloadCmd, generate::GenerateCmd,
|
||||
tip_height::TipHeightCmd, version::VersionCmd,
|
||||
};
|
||||
|
||||
pub use self::start::StartCmd;
|
||||
|
||||
use crate::config::ZebradConfig;
|
||||
|
||||
use abscissa_core::{
|
||||
|
|
|
@ -90,7 +90,7 @@ use crate::{
|
|||
};
|
||||
|
||||
/// `start` subcommand
|
||||
#[derive(Command, Debug, Options)]
|
||||
#[derive(Command, Debug, Options, Default)]
|
||||
pub struct StartCmd {
|
||||
/// Filter strings which override the config file and defaults
|
||||
#[options(free, help = "tracing filters which override the zebrad.toml config")]
|
||||
|
|
|
@ -446,21 +446,6 @@ fn ephemeral(cache_dir_config: EphemeralConfig, cache_dir_check: EphemeralCheck)
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_no_args() -> Result<()> {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
||||
let testdir = testdir()?.with_config(&mut default_test_config()?)?;
|
||||
|
||||
let child = testdir.spawn_child(args![])?;
|
||||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_success()?;
|
||||
|
||||
output.stdout_line_contains("USAGE:")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_no_args() -> Result<()> {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
@ -517,6 +502,37 @@ fn config_test() -> Result<()> {
|
|||
// Check that an older stored configuration we have for Zebra works
|
||||
stored_config_works()?;
|
||||
|
||||
// Runs `zebrad` serially to avoid potential port conflicts
|
||||
app_no_args()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that `zebrad` runs the start command with no args
|
||||
fn app_no_args() -> Result<()> {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
||||
// start caches state, so run one of the start tests with persistent state
|
||||
let testdir = testdir()?.with_config(&mut persistent_test_config()?)?;
|
||||
|
||||
let mut child = testdir.spawn_child(args![])?;
|
||||
|
||||
// Run the program and kill it after a few seconds
|
||||
std::thread::sleep(LAUNCH_DELAY);
|
||||
child.kill(true)?;
|
||||
|
||||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_failure()?;
|
||||
|
||||
output.stdout_line_contains("Starting zebrad")?;
|
||||
|
||||
// Make sure the command passed the legacy chain check
|
||||
output.stdout_line_contains("starting legacy chain check")?;
|
||||
output.stdout_line_contains("no legacy chain found")?;
|
||||
|
||||
// Make sure the command was killed
|
||||
output.assert_was_killed()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue