Skip to content

Commit

Permalink
adds start as default subcommand for zebrad (#4957)
Browse files Browse the repository at this point in the history
* 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 61ce46f.

* 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>
  • Loading branch information
3 people authored Aug 30, 2022
1 parent ecf2d80 commit 3ff56c2
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 20 deletions.
2 changes: 1 addition & 1 deletion zebrad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
7 changes: 5 additions & 2 deletions zebrad/src/application.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Zebrad Abscissa Application
mod entry_point;
use self::entry_point::EntryPoint;

use std::{fmt::Write as _, io::Write as _, process};

use abscissa_core::{
application::{self, fatal_error, AppCell},
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;
Expand Down Expand Up @@ -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;
Expand Down
104 changes: 104 additions & 0 deletions zebrad/src/application/entry_point.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
4 changes: 3 additions & 1 deletion zebrad/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
2 changes: 1 addition & 1 deletion zebrad/src/commands/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
46 changes: 31 additions & 15 deletions zebrad/tests/acceptance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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(())
}

Expand Down

0 comments on commit 3ff56c2

Please sign in to comment.