From 1d4e7ba6b82bc3248e34ffe0e477ca04e16c6c2e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 22 Aug 2022 12:29:59 +1000 Subject: [PATCH] Add DumpConfigs --- validator_manager/src/lib.rs | 29 ++++++++++++++++++- .../src/validators/create_validators.rs | 17 ++++++++--- .../src/validators/import_validators.rs | 15 ++++++++-- validator_manager/src/validators/mod.rs | 8 +++-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/validator_manager/src/lib.rs b/validator_manager/src/lib.rs index 88bdbd96dd..79d542e6c2 100644 --- a/validator_manager/src/lib.rs +++ b/validator_manager/src/lib.rs @@ -1,12 +1,36 @@ use clap::App; use clap::ArgMatches; use environment::Environment; +use serde::Serialize; +use std::path::PathBuf; use types::EthSpec; +use validators::create_validators::write_to_json_file; mod validators; pub const CMD: &str = "validator_manager"; +/// This flag is on the top-level `lighthouse` binary. +const DUMP_CONFIGS_FLAG: &str = "dump-configs"; + +/// Used only in testing, this allows a command to dump its configuration to a file and then exit +/// successfully. This allows for testing how the CLI arguments translate to some configuration. +pub enum DumpConfigs { + Disabled, + Enabled(PathBuf), +} + +impl DumpConfigs { + /// Returns `Ok(true)` if the configuration was successfully written to a file and the + /// application should exit successfully without doing anything else. + pub fn should_exit_early(&self, config: &T) -> Result { + match self { + DumpConfigs::Disabled => Ok(false), + DumpConfigs::Enabled(dump_path) => write_to_json_file(dump_path, config).map(|()| true), + } + } +} + pub fn cli_app<'a, 'b>() -> App<'a, 'b> { App::new(CMD) .visible_aliases(&["vm", CMD]) @@ -21,6 +45,9 @@ pub fn run<'a, T: EthSpec>( ) -> Result<(), String> { let context = env.core_context(); let spec = context.eth2_config.spec.clone(); + let dump_configs = clap_utils::parse_optional(matches, DUMP_CONFIGS_FLAG)? + .map(DumpConfigs::Enabled) + .unwrap_or_else(|| DumpConfigs::Disabled); context .executor @@ -31,7 +58,7 @@ pub fn run<'a, T: EthSpec>( async { match matches.subcommand() { (validators::CMD, Some(matches)) => { - validators::cli_run::(matches, &spec).await + validators::cli_run::(matches, &spec, dump_configs).await } (unknown, _) => Err(format!( "{} is not a valid {} command. See --help.", diff --git a/validator_manager/src/validators/create_validators.rs b/validator_manager/src/validators/create_validators.rs index 0c5869c4a8..e3e00481ee 100644 --- a/validator_manager/src/validators/create_validators.rs +++ b/validator_manager/src/validators/create_validators.rs @@ -1,4 +1,5 @@ use super::common::*; +use crate::DumpConfigs; use account_utils::{random_password_string, read_mnemonic_from_cli, read_password_from_user}; use clap::{App, Arg, ArgMatches}; use eth2::{ @@ -7,7 +8,7 @@ use eth2::{ BeaconNodeHttpClient, SensitiveUrl, Timeouts, }; use eth2_wallet::WalletBuilder; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -180,7 +181,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { /// The CLI arguments are parsed into this struct before running the application. This step of /// indirection allows for testing the underlying logic without needing to parse CLI arguments. -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] pub struct CreateConfig { pub output_path: PathBuf, pub first_index: u32, @@ -460,9 +461,14 @@ impl ValidatorsAndDeposits { pub async fn cli_run<'a, T: EthSpec>( matches: &'a ArgMatches<'a>, spec: &ChainSpec, + dump_configs: DumpConfigs, ) -> Result<(), String> { let config = CreateConfig::from_cli(matches, spec)?; - run::(config, spec).await + if dump_configs.should_exit_early(&config)? { + Ok(()) + } else { + run::(config, spec).await + } } async fn run<'a, T: EthSpec>(config: CreateConfig, spec: &ChainSpec) -> Result<(), String> { @@ -506,7 +512,10 @@ async fn run<'a, T: EthSpec>(config: CreateConfig, spec: &ChainSpec) -> Result<( /// Write some object to a file as JSON. /// /// The file must be created new, it must not already exist. -fn write_to_json_file, S: Serialize>(path: P, contents: &S) -> Result<(), String> { +pub fn write_to_json_file, S: Serialize>( + path: P, + contents: &S, +) -> Result<(), String> { eprintln!("Writing {:?}", path.as_ref()); let mut file = fs::OpenOptions::new() .write(true) diff --git a/validator_manager/src/validators/import_validators.rs b/validator_manager/src/validators/import_validators.rs index d1c237ca5a..418b561b6c 100644 --- a/validator_manager/src/validators/import_validators.rs +++ b/validator_manager/src/validators/import_validators.rs @@ -1,4 +1,5 @@ use super::common::*; +use crate::DumpConfigs; use clap::{App, Arg, ArgMatches}; use eth2::{ lighthouse_vc::{ @@ -7,6 +8,7 @@ use eth2::{ }, SensitiveUrl, }; +use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; @@ -72,7 +74,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { ) } -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize)] struct ImportConfig { validators_file_path: PathBuf, vc_url: SensitiveUrl, @@ -91,9 +93,16 @@ impl ImportConfig { } } -pub async fn cli_run<'a>(matches: &'a ArgMatches<'a>) -> Result<(), String> { +pub async fn cli_run<'a>( + matches: &'a ArgMatches<'a>, + dump_configs: DumpConfigs, +) -> Result<(), String> { let config = ImportConfig::from_cli(matches)?; - run(config).await + if dump_configs.should_exit_early(&config)? { + Ok(()) + } else { + run(config).await + } } async fn run<'a>(config: ImportConfig) -> Result<(), String> { diff --git a/validator_manager/src/validators/mod.rs b/validator_manager/src/validators/mod.rs index 04ff692401..1b6b769378 100644 --- a/validator_manager/src/validators/mod.rs +++ b/validator_manager/src/validators/mod.rs @@ -2,6 +2,7 @@ pub mod common; pub mod create_validators; pub mod import_validators; +use crate::DumpConfigs; use clap::{App, ArgMatches}; use types::{ChainSpec, EthSpec}; @@ -17,12 +18,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { pub async fn cli_run<'a, T: EthSpec>( matches: &'a ArgMatches<'a>, spec: &ChainSpec, + dump_configs: DumpConfigs, ) -> Result<(), String> { match matches.subcommand() { (create_validators::CMD, Some(matches)) => { - create_validators::cli_run::(matches, spec).await + create_validators::cli_run::(matches, spec, dump_configs).await + } + (import_validators::CMD, Some(matches)) => { + import_validators::cli_run(matches, dump_configs).await } - (import_validators::CMD, Some(matches)) => import_validators::cli_run(matches).await, (unknown, _) => Err(format!( "{} does not have a {} command. See --help", CMD, unknown