diff --git a/masq_lib/src/multi_config.rs b/masq_lib/src/multi_config.rs index 8cc6574f5..5c87da2c8 100644 --- a/masq_lib/src/multi_config.rs +++ b/masq_lib/src/multi_config.rs @@ -50,6 +50,7 @@ macro_rules! values_m { #[derive(Debug)] pub struct MultiConfig<'a> { arg_matches: ArgMatches<'a>, + computed_value_names: HashSet, } impl<'a> MultiConfig<'a> { @@ -63,9 +64,20 @@ impl<'a> MultiConfig<'a> { ) -> Result, ConfiguratorError> { let initial: Box = Box::new(CommandLineVcl::new(vec![String::new()])); - let merged: Box = vcls + let mut computed_value_names = HashSet::new(); + vcls.iter().for_each(|vcl| { + vcl.vcl_args().iter().for_each(|vcl_arg| { + match vcl.is_computed() { + true => computed_value_names.insert(vcl_arg.name().to_string()), + false => computed_value_names.remove(&vcl_arg.name().to_string()), + }; + }) + }); + // TODO pull this out to function to use in determine_user_specific_data + let merged = vcls .into_iter() .fold(initial, |so_far, vcl| merge(so_far, vcl)); + let arg_matches = match schema .clone() .get_matches_from_safe(merged.args().into_iter()) @@ -78,7 +90,10 @@ impl<'a> MultiConfig<'a> { _ => return Err(Self::make_configurator_error(e)), }, }; - Ok(MultiConfig { arg_matches }) + Ok(MultiConfig { + arg_matches, + computed_value_names, + }) } fn check_for_invalid_value_err( @@ -139,6 +154,10 @@ impl<'a> MultiConfig<'a> { ConfiguratorError::required("", &format!("Unfamiliar message: {}", e.message)) } + pub fn is_user_specified(&self, value_name: &str) -> bool { + !self.computed_value_names.contains(value_name) + } + pub fn occurrences_of(&self, parameter: &str) -> u64 { self.arg_matches.occurrences_of(parameter) } @@ -150,6 +169,7 @@ impl<'a> MultiConfig<'a> { pub trait VclArg: Debug { fn name(&self) -> &str; + fn value_opt(&self) -> Option<&str>; fn to_args(&self) -> Vec; fn dup(&self) -> Box; } @@ -175,7 +195,9 @@ impl VclArg for NameValueVclArg { fn name(&self) -> &str { &self.name } - + fn value_opt(&self) -> Option<&str> { + Some(self.value.as_str()) + } fn to_args(&self) -> Vec { vec![self.name.clone(), self.value.clone()] } @@ -203,7 +225,9 @@ impl VclArg for NameOnlyVclArg { fn name(&self) -> &str { &self.name } - + fn value_opt(&self) -> Option<&str> { + None + } fn to_args(&self) -> Vec { vec![self.name.clone()] } @@ -224,6 +248,9 @@ impl NameOnlyVclArg { pub trait VirtualCommandLine { fn vcl_args(&self) -> Vec<&dyn VclArg>; fn args(&self) -> Vec; + fn is_computed(&self) -> bool { + false + } } impl Debug for dyn VirtualCommandLine { @@ -263,6 +290,24 @@ pub fn merge( }) } +pub struct ComputedVcl { + vcl_args: Vec>, +} + +impl VirtualCommandLine for ComputedVcl { + fn vcl_args(&self) -> Vec<&dyn VclArg> { + vcl_args_to_vcl_args(&self.vcl_args) + } + + fn args(&self) -> Vec { + vcl_args_to_args(&self.vcl_args) + } + + fn is_computed(&self) -> bool { + true + } +} + pub struct CommandLineVcl { vcl_args: Vec>, } @@ -283,6 +328,33 @@ impl From>> for CommandLineVcl { } } +impl ComputedVcl { + pub fn new(mut args: Vec) -> ComputedVcl { + args.remove(0); // remove command + let mut vcl_args = vec![]; + while let Some(vcl_arg) = Self::next_vcl_arg(&mut args) { + vcl_args.push(vcl_arg); + } + ComputedVcl { vcl_args } + } + + fn next_vcl_arg(args: &mut Vec) -> Option> { + if args.is_empty() { + return None; + } + let name = args.remove(0); + if !name.starts_with("--") { + panic!("Expected option beginning with '--', not {}", name) + } + if args.is_empty() || args[0].starts_with("--") { + Some(Box::new(NameOnlyVclArg::new(&name))) + } else { + let value = args.remove(0); + Some(Box::new(NameValueVclArg::new(&name, &value))) + } + } +} + impl CommandLineVcl { pub fn new(mut args: Vec) -> CommandLineVcl { args.remove(0); // remove command @@ -334,7 +406,7 @@ impl EnvironmentVcl { .collect(); let mut vcl_args: Vec> = vec![]; for (upper_name, value) in std::env::vars() { - if (upper_name.len() < 5) || (&upper_name[0..5] != "MASQ_") { + if (upper_name.len() < 5) || (&upper_name[0..5] != "MASQ_") || (value == *"") { continue; } let lower_name = str::replace(&upper_name[5..].to_lowercase(), "_", "-"); @@ -347,10 +419,19 @@ impl EnvironmentVcl { } } +#[derive(Debug)] pub struct ConfigFileVcl { vcl_args: Vec>, } +impl Clone for ConfigFileVcl { + fn clone(&self) -> Self { + ConfigFileVcl { + vcl_args: self.vcl_args.iter().map(|arg| arg.dup()).collect(), + } + } +} + impl VirtualCommandLine for ConfigFileVcl { fn vcl_args(&self) -> Vec<&dyn VclArg> { vcl_args_to_vcl_args(&self.vcl_args) @@ -491,8 +572,14 @@ fn append(ts: Vec, t: T) -> Vec { #[cfg(not(feature = "no_test_share"))] impl<'a> MultiConfig<'a> { - pub fn new_test_only(arg_matches: ArgMatches<'a>) -> Self { - Self { arg_matches } + pub fn new_test_only( + arg_matches: ArgMatches<'a>, + computed_value_names: HashSet, + ) -> Self { + Self { + arg_matches, + computed_value_names, + } } } @@ -504,6 +591,7 @@ pub mod tests { use clap::Arg; use std::fs::File; use std::io::Write; + use std::ops::Deref; #[test] fn config_file_vcl_error_displays_open_error() { @@ -949,6 +1037,40 @@ pub mod tests { assert_eq!(subject.args(), command_line); } + #[test] + fn command_line_vcl_return_value_from_vcl_args_by_name() { + let command_line: Vec = vec![ + "", + "--first-value", + "/nonexistent/directory", + "--takes_no_value", + "--other_takes_no_value", + ] + .into_iter() + .map(|s| s.to_string()) + .collect(); + + let subject = CommandLineVcl::new(command_line.clone()); + let existing_value = match subject + .vcl_args + .iter() + .find(|vcl_arg_box| vcl_arg_box.deref().name() == "--first-value") + { + Some(vcl_arg_box) => vcl_arg_box.deref().value_opt(), + None => None, + }; + let non_existing_value = match subject + .vcl_args + .iter() + .find(|vcl_arg_box| vcl_arg_box.deref().name() == "--takes_no_value") + { + Some(vcl_arg_box) => vcl_arg_box.deref().value_opt(), + None => None, + }; + assert_eq!(existing_value.unwrap(), "/nonexistent/directory"); + assert_eq!(non_existing_value, None); + } + #[test] #[should_panic(expected = "Expected option beginning with '--', not value")] fn command_line_vcl_panics_when_given_value_without_name() { @@ -963,12 +1085,19 @@ pub mod tests { #[test] fn environment_vcl_works() { let _guard = EnvironmentGuard::new(); - let schema = App::new("test").arg( - Arg::with_name("numeric-arg") - .long("numeric-arg") - .takes_value(true), - ); + let schema = App::new("test") + .arg( + Arg::with_name("numeric-arg") + .long("numeric-arg") + .takes_value(true), + ) + .arg( + Arg::with_name("empty-arg") + .long("empty-arg") + .takes_value(true), + ); std::env::set_var("MASQ_NUMERIC_ARG", "47"); + std::env::set_var("MASQ_EMPTY_ARG", ""); let subject = EnvironmentVcl::new(&schema); diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index f336864e9..20980f02c 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -18,7 +18,7 @@ use crate::node_configurator::unprivileged_parse_args_configuration::{ UnprivilegedParseArgsConfigurationDaoReal, }; use crate::node_configurator::{ - data_directory_from_context, determine_fundamentals, DirsWrapper, DirsWrapperReal, + data_directory_from_context, determine_user_specific_data, DirsWrapper, DirsWrapperReal, }; use crate::sub_lib::accountant::PaymentThresholds as PaymentThresholdsFromAccountant; use crate::sub_lib::accountant::DEFAULT_SCAN_INTERVALS; @@ -495,9 +495,12 @@ impl SetupReporterReal { Some(command_line) => command_line, None => vec![], }; - let (config_file_path, user_specified, _data_directory, _real_user) = - determine_fundamentals(dirs_wrapper, &app, &command_line)?; - let config_file_vcl = match ConfigFileVcl::new(&config_file_path, user_specified) { + let user_specific_data = + determine_user_specific_data(dirs_wrapper, &app, &command_line)?; + let config_file_vcl = match ConfigFileVcl::new( + &user_specific_data.config_file, + user_specific_data.config_file_spec, + ) { Ok(cfv) => cfv, Err(e) => return Err(ConfiguratorError::required("config-file", &e.to_string())), }; diff --git a/node/src/node_configurator/mod.rs b/node/src/node_configurator/mod.rs index e2baff293..bda38980b 100644 --- a/node/src/node_configurator/mod.rs +++ b/node/src/node_configurator/mod.rs @@ -11,34 +11,218 @@ use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::db_config::persistent_configuration::{ PersistentConfiguration, PersistentConfigurationReal, }; -use crate::sub_lib::utils::{db_connection_launch_panic, make_new_multi_config}; +use crate::sub_lib::utils::db_connection_launch_panic; use clap::{value_t, App}; +use core::option::Option; use dirs::{data_local_dir, home_dir}; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::DEFAULT_CHAIN; use masq_lib::multi_config::{merge, CommandLineVcl, EnvironmentVcl, MultiConfig, VclArg}; -use masq_lib::shared_schema::{ - chain_arg, config_file_arg, data_directory_arg, real_user_arg, ConfiguratorError, - DATA_DIRECTORY_HELP, -}; +use masq_lib::shared_schema::ConfiguratorError; use masq_lib::utils::{add_masq_and_chain_directories, localhost}; +use std::env::current_dir; use std::net::{SocketAddr, TcpListener}; +use std::ops::Deref; use std::path::{Path, PathBuf}; pub trait NodeConfigurator { fn configure(&self, multi_config: &MultiConfig) -> Result; } -pub fn determine_fundamentals( +#[derive(Debug)] +pub struct UserSpecifiedData { + pub(crate) chain: Chain, + pub(crate) chain_spec: bool, + pub(crate) real_user: RealUser, + pub(crate) real_user_spec: bool, + pub(crate) data_directory: PathBuf, + pub(crate) data_directory_spec: bool, + pub(crate) config_file: PathBuf, + pub(crate) config_file_spec: bool, +} + +fn get_chain_from_vcl(configs: &[Box]) -> (Chain, bool) { + match argument_from_vcl(configs, "--chain") { + Some(chain) => (Chain::from(chain), true), + None => (DEFAULT_CHAIN, false), + } +} + +fn get_real_user_from_vcl( + configs: &[Box], + dirs_wrapper: &dyn DirsWrapper, +) -> (RealUser, bool) { + match argument_from_vcl(configs, "--real-user") { + Some(user) => { + let real_user_split: Vec<&str> = user.split(':').collect(); + ( + RealUser::new( + Some(real_user_split[0].parse::().expect("expected user id")), + Some( + real_user_split[1] + .parse::() + .expect("expected user group"), + ), + Some(PathBuf::from(real_user_split[2])), + ), + true, + ) + } + None => { + #[cfg(target_os = "windows")] + { + ( + RealUser::new(Some(999999), Some(999999), None).populate(dirs_wrapper), + false, + ) + } + #[cfg(not(target_os = "windows"))] + { + ( + RealUser::new(None, None, None).populate(dirs_wrapper), + false, + ) + } + } + } +} + +fn get_data_directory_from_vcl( + configs: &[Box], + dirs_wrapper: &dyn DirsWrapper, + real_user: &RealUser, + chain: &Chain, +) -> (PathBuf, bool) { + match argument_from_vcl(configs, "--data-directory") { + Some(data_dir) => match PathBuf::from(data_dir).starts_with("~/") { + true => { + let home_dir_from_wrapper = dirs_wrapper + .home_dir() + .expect("expexted users home dir") + .to_str() + .expect("expect home dir") + .to_string(); + let replaced_tilde_dir = + data_dir + .to_string() + .replacen('~', home_dir_from_wrapper.as_str(), 1); + (PathBuf::from(replaced_tilde_dir), true) + } + false => (PathBuf::from(&data_dir), true), + }, + None => ( + data_directory_from_context(dirs_wrapper, real_user, *chain), + false, + ), + } +} + +fn get_config_file_from_vcl( + configs: &[Box], + data_directory: &PathBuf, + data_directory_def: bool, + dirs_wrapper: &dyn DirsWrapper, +) -> (PathBuf, bool) { + match argument_from_vcl(configs, "--config-file") { + Some(config_str) => { + let path = match PathBuf::from(config_str).is_relative() { + true => { + match PathBuf::from(config_str).file_name().expect("expected file name") == config_str { + true => PathBuf::from(data_directory).join(PathBuf::from(config_str)), + false => match PathBuf::from(config_str).starts_with("./") || PathBuf::from(config_str).starts_with("../") { + true => current_dir().expect("expected curerrnt dir").join(PathBuf::from(config_str)), + false => match PathBuf::from(config_str).starts_with("~") { + true => { + let home_dir_from_wrapper = dirs_wrapper + .home_dir() + .expect("expexted users home_dir") + .to_str() + .expect("expect str home_dir") + .to_string(); + let replaced_tilde_dir = + config_str + .to_string() + .replacen('~', home_dir_from_wrapper.as_str(), 1); + PathBuf::from(replaced_tilde_dir) + } + false => match data_directory_def { + true => PathBuf::from(data_directory).join(PathBuf::from(config_str)), + false => panic!("You need to define data-directory to define config file with naked directory 'dirname/config.toml'.") + } + } + } + } + } + false => PathBuf::from(config_str), + }; + (path, true) + } + None => { + let path = PathBuf::from(data_directory).join(PathBuf::from("config.toml")); + match path.is_file() { + true => (path, true), + false => (path, false), + } + } + } +} + +fn config_file_data_dir_real_user_chain_from_vcl( + dirs_wrapper: &dyn DirsWrapper, + configs: Vec>, +) -> UserSpecifiedData { + let mut user_specified_data = UserSpecifiedData { + chain: Default::default(), + chain_spec: false, + real_user: Default::default(), + real_user_spec: false, + data_directory: Default::default(), + data_directory_spec: false, + config_file: Default::default(), + config_file_spec: false, + }; + let configs = configs.as_slice(); + (user_specified_data.chain, user_specified_data.chain_spec) = get_chain_from_vcl(configs); + ( + user_specified_data.real_user, + user_specified_data.real_user_spec, + ) = get_real_user_from_vcl(configs, dirs_wrapper); + ( + user_specified_data.data_directory, + user_specified_data.data_directory_spec, + ) = get_data_directory_from_vcl( + configs, + dirs_wrapper, + &user_specified_data.real_user, + &user_specified_data.chain, + ); + ( + user_specified_data.config_file, + user_specified_data.config_file_spec, + ) = get_config_file_from_vcl( + configs, + &user_specified_data.data_directory, + user_specified_data.data_directory_spec, + dirs_wrapper, + ); + user_specified_data +} + +fn argument_from_vcl<'a>(configs: &'a [Box], needle: &'a str) -> Option<&'a str> { + match configs + .iter() + .find(|vcl_arg_box| vcl_arg_box.deref().name() == needle) + { + Some(vcl_arg_box) => vcl_arg_box.deref().value_opt(), + None => None, + } +} + +pub fn determine_user_specific_data( dirs_wrapper: &dyn DirsWrapper, app: &App, args: &[String], -) -> Result<(PathBuf, bool, PathBuf, RealUser), ConfiguratorError> { - let orientation_schema = App::new("MASQNode") - .arg(chain_arg()) - .arg(real_user_arg()) - .arg(data_directory_arg(DATA_DIRECTORY_HELP)) - .arg(config_file_arg()); +) -> Result { let orientation_args: Vec> = merge( Box::new(EnvironmentVcl::new(app)), Box::new(CommandLineVcl::new(args.to_vec())), @@ -53,24 +237,11 @@ pub fn determine_fundamentals( }) .map(|vcl_arg| vcl_arg.dup()) .collect(); - let orientation_vcl = CommandLineVcl::from(orientation_args); - let multi_config = make_new_multi_config(&orientation_schema, vec![Box::new(orientation_vcl)])?; - let config_file_path = value_m!(multi_config, "config-file", PathBuf) - .expect("config-file parameter is not properly defaulted by clap"); - let user_specified = multi_config.occurrences_of("config-file") > 0; - let (real_user, data_directory_path, chain) = - real_user_data_directory_path_and_chain(dirs_wrapper, &multi_config); - let data_directory = match data_directory_path { - Some(data_dir) => data_dir, - None => data_directory_from_context(dirs_wrapper, &real_user, chain), - }; - let config_file_path = if config_file_path.is_relative() { - data_directory.join(config_file_path) - } else { - config_file_path - }; + //TODO probably need to implement new merge from config_vcl with orientation_args + let user_specified_data = + config_file_data_dir_real_user_chain_from_vcl(dirs_wrapper, orientation_args); - Ok((config_file_path, user_specified, data_directory, real_user)) + Ok(user_specified_data) } pub fn initialize_database( @@ -159,6 +330,7 @@ mod tests { use super::*; use crate::node_test_utils::DirsWrapperMock; use crate::test_utils::ArgsBuilder; + use masq_lib::shared_schema::{config_file_arg, data_directory_arg, DATA_DIRECTORY_HELP}; use masq_lib::test_utils::environment_guard::EnvironmentGuard; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::find_free_port; @@ -211,18 +383,27 @@ mod tests { .param("--config-file", "booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), ) .unwrap(); assert_eq!( - &format!("{}", config_file_path.parent().unwrap().display()), - &data_directory.to_string_lossy().to_string(), + &format!( + "{}", + user_specific_data.config_file.parent().unwrap().display() + ), + &user_specific_data + .data_directory + .to_string_lossy() + .to_string(), ); - assert_eq!("booga.toml", config_file_path.file_name().unwrap()); - assert_eq!(true, user_specified); + assert_eq!( + "booga.toml", + user_specific_data.config_file.file_name().unwrap() + ); + assert_eq!(user_specific_data.real_user_spec, false); } #[test] @@ -240,18 +421,27 @@ mod tests { ); std::env::set_var("MASQ_CONFIG_FILE", "booga.toml"); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), ) .unwrap(); assert_eq!( - format!("{}", config_file_path.parent().unwrap().display()), - data_directory.to_string_lossy().to_string(), + format!( + "{}", + user_specific_data.config_file.parent().unwrap().display() + ), + user_specific_data + .data_directory + .to_string_lossy() + .to_string(), + ); + assert_eq!( + "booga.toml", + user_specific_data.config_file.file_name().unwrap() ); - assert_eq!("booga.toml", config_file_path.file_name().unwrap()); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); //all these assertions of 'real_user_specified' was incorrect, no idea how this tests could pass before } #[cfg(not(target_os = "windows"))] @@ -263,7 +453,7 @@ mod tests { .param("--config-file", "/tmp/booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -272,9 +462,9 @@ mod tests { assert_eq!( "/tmp/booga.toml", - &format!("{}", config_file_path.display()) + &format!("{}", user_specific_data.config_file.display()) ); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); } #[cfg(target_os = "windows")] @@ -286,7 +476,7 @@ mod tests { .param("--config-file", r"\tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -295,9 +485,9 @@ mod tests { assert_eq!( r"\tmp\booga.toml", - &format!("{}", config_file_path.display()) + &format!("{}", user_specific_data.config_file.display()) ); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); } #[cfg(target_os = "windows")] @@ -309,7 +499,7 @@ mod tests { .param("--config-file", r"c:\tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -318,9 +508,9 @@ mod tests { assert_eq!( r"c:\tmp\booga.toml", - &format!("{}", config_file_path.display()) + &format!("{}", user_specific_data.config_file.display()) ); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); } #[cfg(target_os = "windows")] @@ -332,7 +522,7 @@ mod tests { .param("--config-file", r"\\TMP\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -341,9 +531,9 @@ mod tests { assert_eq!( r"\\TMP\booga.toml", - &format!("{}", config_file_path.display()) + &format!("{}", user_specific_data.config_file.display()) ); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); } #[cfg(target_os = "windows")] @@ -356,7 +546,7 @@ mod tests { .param("--config-file", r"c:tmp\booga.toml"); let args_vec: Vec = args.into(); - let (config_file_path, user_specified, _data_dir, _real_user) = determine_fundamentals( + let user_specific_data = determine_user_specific_data( &DirsWrapperReal {}, &determine_config_file_path_app(), args_vec.as_slice(), @@ -365,9 +555,9 @@ mod tests { assert_eq!( r"c:tmp\booga.toml", - &format!("{}", config_file_path.display()) + &format!("{}", user_specific_data.config_file.display()) ); - assert_eq!(true, user_specified); + assert_eq!(user_specific_data.real_user_spec, false); } #[test] diff --git a/node/src/node_configurator/node_configurator_standard.rs b/node/src/node_configurator/node_configurator_standard.rs index 1094f7f18..c257b1f23 100644 --- a/node/src/node_configurator/node_configurator_standard.rs +++ b/node/src/node_configurator/node_configurator_standard.rs @@ -1,16 +1,16 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::bootstrapper::BootstrapperConfig; -use crate::node_configurator::DirsWrapperReal; use crate::node_configurator::{initialize_database, DirsWrapper, NodeConfigurator}; +use crate::node_configurator::{DirsWrapperReal, UserSpecifiedData}; use masq_lib::crash_point::CrashPoint; use masq_lib::logger::Logger; -use masq_lib::multi_config::MultiConfig; +use masq_lib::multi_config::{ComputedVcl, MultiConfig, VirtualCommandLine}; use masq_lib::shared_schema::ConfiguratorError; use masq_lib::utils::NeighborhoodModeLight; use std::net::SocketAddr; use std::net::{IpAddr, Ipv4Addr}; -use std::path::PathBuf; +use std::ops::Deref; use clap::value_t; use log::LevelFilter; @@ -24,13 +24,14 @@ use crate::node_configurator::unprivileged_parse_args_configuration::{ UnprivilegedParseArgsConfiguration, UnprivilegedParseArgsConfigurationDaoReal, }; use crate::node_configurator::{ - data_directory_from_context, determine_fundamentals, real_user_data_directory_path_and_chain, + data_directory_from_context, determine_user_specific_data, + real_user_data_directory_path_and_chain, }; -use crate::server_initializer::GatheredParams; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::utils::make_new_multi_config; use crate::tls_discriminator_factory::TlsDiscriminatorFactory; +use itertools::Itertools; use masq_lib::constants::{DEFAULT_UI_PORT, HTTP_PORT, TLS_PORT}; use masq_lib::multi_config::{CommandLineVcl, ConfigFileVcl, EnvironmentVcl}; use std::str::FromStr; @@ -136,35 +137,152 @@ fn collect_externals_from_multi_config( ) } +fn extract_values_vcl_fill_multiconfig_vec( + config_file_vcl: Box, + environment_vcl: Box, + commandline_vcl: Box, + user_specific_data: UserSpecifiedData, +) -> (Vec, Vec) { + let mut unspecified_vec: Vec = vec!["".to_string()]; + let mut specified_vec: Vec = vec!["".to_string()]; + let config_file_specified = user_specific_data.config_file_spec; + let config_file_path = user_specific_data.config_file; + let extract_value_from_vcl = + |vcl: &dyn VirtualCommandLine, name: &str, var: &str, spec: bool| { + let args = vcl.args(); + let extracted_args = args + .iter() + .enumerate() + .filter(|(_index, vcl_arg)| vcl_arg.deref() == &name.to_string()) + .collect_vec(); + match extracted_args.is_empty() { + false => { + let val_str = args[extracted_args[0].0 + 1].clone(); + (val_str, true) + } + true => (var.to_string(), spec), + } + }; + let (cf_real_user, cf_real_user_specified) = extract_value_from_vcl( + &*config_file_vcl, + "--real-user", + user_specific_data.real_user.to_string().as_str(), + user_specific_data.real_user_spec, + ); + + let (env_real_user, env_real_user_specified) = extract_value_from_vcl( + &*environment_vcl, + "--real-user", + user_specific_data.real_user.to_string().as_str(), + user_specific_data.real_user_spec, + ); + + let (cmd_real_user, cmd_real_user_specified) = extract_value_from_vcl( + &*commandline_vcl, + "--real-user", + user_specific_data.real_user.to_string().as_str(), + user_specific_data.real_user_spec, + ); + + let mut fill_specified_or_unspecified_box = + |key: &str, value: &str, specified: bool| match value.is_empty() { + true => (), + false => match specified { + true => match specified_vec.contains(&key.to_string()) { + true => { + let index = specified_vec + .iter() + .position(|r| r == key) + .expect("expected index of vcl name") + + 1; + specified_vec[index] = value.to_string(); + } + false => { + specified_vec.push(key.to_string()); + specified_vec.push(value.to_string()); + } + }, + false => match unspecified_vec.contains(&key.to_string()) { + true => { + let index = unspecified_vec + .iter() + .position(|r| r == key) + .expect("expected index of vcl name") + + 1; + unspecified_vec[index] = value.to_string(); + } + false => { + unspecified_vec.push(key.to_string()); + unspecified_vec.push(value.to_string()); + } + }, + }, + }; + fill_specified_or_unspecified_box( + "--config-file", + config_file_path.as_path().to_string_lossy().as_ref(), + config_file_specified, + ); + fill_specified_or_unspecified_box( + "--data-directory", + user_specific_data.data_directory.to_string_lossy().as_ref(), + user_specific_data.data_directory_spec, + ); + + fill_specified_or_unspecified_box("--real-user", cf_real_user.as_str(), cf_real_user_specified); + + fill_specified_or_unspecified_box( + "--real-user", + env_real_user.as_str(), + env_real_user_specified, + ); + + fill_specified_or_unspecified_box( + "--real-user", + cmd_real_user.as_str(), + cmd_real_user_specified, + ); + (unspecified_vec, specified_vec) +} + pub fn server_initializer_collected_params<'a>( dirs_wrapper: &dyn DirsWrapper, args: &[String], -) -> Result, ConfiguratorError> { +) -> Result, ConfiguratorError> { let app = app_node(); - - let (config_file_path, user_specified, data_directory, real_user) = - determine_fundamentals(dirs_wrapper, &app, args)?; - - let config_file_vcl = match ConfigFileVcl::new(&config_file_path, user_specified) { - Ok(cfv) => Box::new(cfv), + let user_specific_data = determine_user_specific_data(dirs_wrapper, &app, args)?; + let config_file_vcl = match ConfigFileVcl::new( + &user_specific_data.config_file, + user_specific_data.config_file_spec, + ) { + Ok(cfv) => cfv, Err(e) => return Err(ConfiguratorError::required("config-file", &e.to_string())), }; - let full_multi_config = make_new_multi_config( - &app, - vec![ - Box::new(CommandLineVcl::new(args.to_vec())), - Box::new(EnvironmentVcl::new(&app)), - config_file_vcl, - ], - )?; - let config_file_path = - value_m!(full_multi_config, "config-file", PathBuf).expect("defaulted param"); - Ok(GatheredParams::new( - full_multi_config, - config_file_path, - real_user, - data_directory, - )) + + let environment_vcl = EnvironmentVcl::new(&app); + let commandline_vcl = CommandLineVcl::new(args.to_vec()); + let (unspecified_vec, specified_vec) = extract_values_vcl_fill_multiconfig_vec( + Box::new(config_file_vcl.clone()), + Box::new(EnvironmentVcl::new(&app)), + Box::new(CommandLineVcl::new(commandline_vcl.args())), + user_specific_data, + ); + let mut multi_config_args_vec: Vec> = vec![ + Box::new(config_file_vcl), + Box::new(environment_vcl), + Box::new(commandline_vcl), + ]; + //TODO write test for MultiConfig "try_new" merge line 76 + if unspecified_vec.len() > 1 { + multi_config_args_vec.push(Box::new(ComputedVcl::new(unspecified_vec))); + } + if specified_vec.len() > 1 { + multi_config_args_vec.push(Box::new(CommandLineVcl::new(specified_vec))); + } + + let full_multi_config = make_new_multi_config(&app, multi_config_args_vec)?; + + Ok(full_multi_config) } pub fn establish_port_configurations(config: &mut BootstrapperConfig) { @@ -310,10 +428,11 @@ mod tests { use masq_lib::multi_config::VirtualCommandLine; use masq_lib::test_utils::environment_guard::{ClapGuard, EnvironmentGuard}; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::utils::{running_test, slice_of_strs_to_vec_of_strings}; + use masq_lib::utils::running_test; use rustc_hex::FromHex; use std::convert::TryFrom; - use std::fs::File; + use std::env::current_dir; + use std::fs::{create_dir_all, File}; use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; @@ -461,6 +580,7 @@ mod tests { fn server_initializer_collected_params_can_read_parameters_from_config_file() { running_test(); let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); let home_dir = ensure_node_home_directory_exists( "node_configurator_standard", "server_initializer_collected_params_can_read_parameters_from_config_file", @@ -468,18 +588,20 @@ mod tests { { let mut config_file = File::create(home_dir.join("config.toml")).unwrap(); config_file - .write_all(b"dns-servers = \"111.111.111.111,222.222.222.222\"\n") + .write_all(b"dns-servers = \"111.111.111.111,222.222.222.222\"\nreal-user = \"1004:1004:/home/gooba\"\n") .unwrap(); } + std::env::set_var("MASQ_DNS_SERVERS", ""); + std::env::set_var("MASQ_CONFIG_FILE", ""); let directory_wrapper = make_pre_populated_mocked_directory_wrapper(); - - let gathered_params = server_initializer_collected_params( - &directory_wrapper, - &slice_of_strs_to_vec_of_strings(&["", "--data-directory", home_dir.to_str().unwrap()]), - ) - .unwrap(); - - let multi_config = gathered_params.multi_config; + let args = ArgsBuilder::new().param("--data-directory", home_dir.to_str().unwrap()); + let args_vec: Vec = args.into(); + let multi_config = + server_initializer_collected_params(&directory_wrapper, args_vec.as_slice()).unwrap(); + assert_eq!( + value_m!(multi_config, "data-directory", String).unwrap(), + home_dir.to_str().unwrap() + ); assert_eq!( value_m!(multi_config, "dns-servers", String).unwrap(), "111.111.111.111,222.222.222.222".to_string() @@ -737,6 +859,331 @@ mod tests { assert_eq!(config.crash_point, CrashPoint::Panic); } + fn fill_up_config_file(mut config_file: File) { + { + config_file + .write_all(b"real-user = \"1002:1002:/home/wooga\"\n") + .unwrap(); + config_file + .write_all(b"blockchain-service-url = \"https://www.mainnet2.com\"\n") + .unwrap(); + config_file + .write_all(b"clandestine-port = \"7788\"\n") + .unwrap(); + config_file.write_all(b"consuming-private-key = \"00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF\"\n").unwrap(); + config_file.write_all(b"crash-point = \"None\"\n").unwrap(); + config_file + .write_all(b"dns-servers = \"5.6.7.8\"\n") + .unwrap(); + config_file + .write_all(b"earning-wallet = \"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n") + .unwrap(); + config_file.write_all(b"gas-price = \"77\"\n").unwrap(); + config_file.write_all(b"log-level = \"trace\"\n").unwrap(); + config_file + .write_all(b"mapping-protocol = \"pcp\"\n") + .unwrap(); + config_file.write_all(b"min-hops = \"6\"\n").unwrap(); + config_file + .write_all(b"neighborhood-mode = \"zero-hop\"\n") + .unwrap(); + config_file.write_all(b"scans = \"off\"\n").unwrap(); + config_file.write_all(b"rate-pack = \"2|2|2|2\"\n").unwrap(); + config_file + .write_all(b"payment-thresholds = \"3333|55|33|646|999|999\"\n") + .unwrap(); + config_file + .write_all(b"scan-intervals = \"111|100|99\"\n") + .unwrap() + } + } + + #[test] + fn multi_config_vcl_is_computed_do_right_job() { + running_test(); + let _env_guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( + "node_configurator_standard", + "multi_config_vcl_is_computed_do_right_job", + ); + let data_dir = &home_dir.join("data_dir"); + let config_file_relative = File::create(PathBuf::from("./generated/test/node_configurator_standard/multi_config_vcl_is_computed_do_right_job").join("config.toml")).unwrap(); + fill_up_config_file(config_file_relative); + let env_vec_array = vec![ + ("MASQ_CONFIG_FILE", "./generated/test/node_configurator_standard/multi_config_vcl_is_computed_do_right_job/config.toml"), + #[cfg(not(target_os = "windows"))] + ("MASQ_REAL_USER", "9999:9999:booga"), + ]; + env_vec_array + .clone() + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + let args = ArgsBuilder::new(); + let args_vec: Vec = args.into(); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(home_dir.clone())) + .data_dir_result(Some(data_dir.to_path_buf())); + + let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + let env_multiconfig = result.unwrap(); + + #[cfg(not(target_os = "windows"))] + { + assert_eq!( + value_m!(env_multiconfig, "data-directory", String).unwrap(), + "booga/data_dir/MASQ/polygon-mainnet".to_string() + ); + assert_eq!( + value_m!(env_multiconfig, "config-file", String).unwrap(), + current_dir().expect("expected curerrnt dir") + .join(PathBuf::from( "./generated/test/node_configurator_standard/multi_config_vcl_is_computed_do_right_job/config.toml")) + .to_string_lossy().to_string() + ); + } + #[cfg(target_os = "windows")] + assert_eq!( + value_m!(env_multiconfig, "data-directory", String).unwrap(), + "generated/test/node_configurator_standard/multi_config_vcl_is_computed_do_right_job/home\\data_dir\\MASQ\\polygon-mainnet".to_string() + ); + } + + #[test] + fn server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file() { + running_test(); + let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( + "node_configurator_standard", + "server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file", + ); + let data_dir = &home_dir.join("data_dir"); + let config_file_relative = File::create(PathBuf::from("./generated/test/node_configurator_standard/server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file").join("config.toml")).unwrap(); + fill_up_config_file(config_file_relative); + let env_vec_array = vec![ + ("MASQ_CONFIG_FILE", "./generated/test/node_configurator_standard/server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file/config.toml"), + #[cfg(not(target_os = "windows"))] + ("MASQ_REAL_USER", "9999:9999:booga"), + ]; + env_vec_array + .clone() + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + let args = ArgsBuilder::new(); + let args_vec: Vec = args.into(); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(home_dir.clone())) + .data_dir_result(Some(data_dir.to_path_buf())); + + let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + let env_multiconfig = result.unwrap(); + + assert_eq!(env_multiconfig.is_user_specified("--data-directory"), false); + #[cfg(not(target_os = "windows"))] + { + assert_eq!( + value_m!(env_multiconfig, "data-directory", String).unwrap(), + "booga/data_dir/MASQ/polygon-mainnet".to_string() + ); + assert_eq!( + value_m!(env_multiconfig, "config-file", String).unwrap(), + current_dir().unwrap().join(PathBuf::from( "./generated/test/node_configurator_standard/server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file/config.toml")).to_string_lossy().to_string() + ); + } + #[cfg(target_os = "windows")] + assert_eq!( + value_m!(env_multiconfig, "data-directory", String).unwrap(), + "generated/test/node_configurator_standard/server_initializer_collected_params_handle_dot_in_path_and_real_user_in_config_file/home\\data_dir\\MASQ\\polygon-mainnet".to_string() + ); + } + + #[test] + fn server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file( + ) { + running_test(); + let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file"); + let data_dir = &home_dir.join("masqhome"); + let _create_data_dir = create_dir_all(data_dir); + let config_file_relative = File::create(data_dir.join("config.toml")).unwrap(); + fill_up_config_file(config_file_relative); + let env_vec_array = vec![ + ("MASQ_IP", "9.8.7.6"), + ("MASQ_BLOCKCHAIN-SERVICE-URL", "https://www.mainnet2.com"), + #[cfg(not(target_os = "windows"))] + ("MASQ_REAL_USER", "9999:9999:booga"), + ]; + env_vec_array + .clone() + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + #[cfg(not(target_os = "windows"))] + let args = ArgsBuilder::new() + .param("--blockchain-service-url", "https://www.mainnet1.com") + .param("--config-file", "~/masqhome/config.toml") + .param("--data-directory", "~/masqhome"); + #[cfg(target_os = "windows")] + let args = ArgsBuilder::new() + .param("--blockchain-service-url", "https://www.mainnet1.com") + .param("--config-file", "~/masqhome\\config.toml") + .param("--data-directory", "~/masqhome"); + let args_vec: Vec = args.into(); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(current_dir().expect("expect current directory").join("generated/test/node_configurator_standard/server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file/home"))) + .data_dir_result(Some(data_dir.to_path_buf())); + let result_data_dir = current_dir().expect("expect current directory").join("generated/test/node_configurator_standard/server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file/home/masqhome"); + + let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + let multiconfig = result.unwrap(); + + assert_eq!(multiconfig.is_user_specified("--data-directory"), true); + assert_eq!( + value_m!(multiconfig, "data-directory", String).unwrap(), + result_data_dir.to_string_lossy().to_string() + ); + #[cfg(not(target_os = "windows"))] + { + assert_eq!(multiconfig.is_user_specified("--real-user"), true); + assert_eq!( + value_m!(multiconfig, "real-user", String).unwrap(), + "9999:9999:booga" + ); + } + assert_eq!(multiconfig.is_user_specified("--config-file"), true); + assert_eq!( + value_m!(multiconfig, "config-file", String).unwrap(), + result_data_dir + .join(PathBuf::from("config.toml")) + .to_string_lossy() + .to_string() + ); + assert_eq!(value_m!(multiconfig, "ip", String).unwrap(), "9.8.7.6"); + assert_eq!( + value_m!(multiconfig, "blockchain-service-url", String).unwrap(), + "https://www.mainnet1.com" + ); + } + + #[test] + fn server_initializer_collected_params_handle_config_file_from_environment_and_real_user_from_config_file_with_data_directory( + ) { + running_test(); + let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","server_initializer_collected_params_handle_config_file_from_environment_and_real_user_from_config_file_with_data_directory"); + let data_dir = &home_dir.join("data_dir"); + create_dir_all(home_dir.join("config")).expect("expected directory for config"); + let config_file_relative = File::create(&home_dir.join("config/config.toml")).unwrap(); + fill_up_config_file(config_file_relative); + vec![ + ("MASQ_CONFIG_FILE", "config/config.toml"), + ("MASQ_DATA_DIRECTORY", "/unexistent/directory"), + #[cfg(not(target_os = "windows"))] + ("MASQ_REAL_USER", "999:999:/home/malooga"), + ] + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + let args = ArgsBuilder::new() + .param("--real-user", "1001:1001:cooga") + .param("--data-directory", &home_dir.to_string_lossy().to_string()); + let args_vec: Vec = args.into(); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(home_dir.clone())) + .data_dir_result(Some(data_dir.to_path_buf())); + + let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + let multiconfig = result.unwrap(); + + assert_eq!(multiconfig.is_user_specified("--data-directory"), true); + assert_eq!( + &value_m!(multiconfig, "data-directory", String).unwrap(), + &home_dir.to_string_lossy().to_string() + ); + #[cfg(not(target_os = "windows"))] + assert_eq!( + &value_m!(multiconfig, "real-user", String).unwrap(), + "1001:1001:cooga" + ); + } + + #[test] + #[should_panic( + expected = "You need to define data-directory to define config file with naked directory 'dirname/config.toml'." + )] + fn server_initializer_collected_params_fails_on_naked_dir_config_file_without_data_directory() { + running_test(); + let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","server_initializer_collected_params_handle_config_file_from_environment_and_real_user_from_config_file_with_path_started_by_dot"); + + let data_dir = &home_dir.join("data_dir"); + vec![("MASQ_CONFIG_FILE", "config/config.toml")] + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + let args = ArgsBuilder::new(); + let args_vec: Vec = args.into(); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(home_dir.clone())) + .data_dir_result(Some(data_dir.to_path_buf())); + + let _result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + } + + #[test] + fn server_initializer_collected_params_combine_vlcs_properly() { + running_test(); + let _guard = EnvironmentGuard::new(); + let _clap_guard = ClapGuard::new(); + let home_dir = ensure_node_home_directory_exists( + "node_configurator_standard", + "server_initializer_collected_params_combine_vlcs_properly", + ); + let data_dir = &home_dir.join("data_dir"); + let config_file = File::create(&home_dir.join("config.toml")).unwrap(); + let current_directory = current_dir().unwrap(); + fill_up_config_file(config_file); + + let env_vec_array = vec![ + ("MASQ_CONFIG_FILE", "config.toml"), + ("MASQ_DATA_DIRECTORY", "/nonexistent/directory/home"), + #[cfg(not(target_os = "windows"))] + ("MASQ_REAL_USER", "9999:9999:booga"), + ]; + env_vec_array + .clone() + .into_iter() + .for_each(|(name, value)| std::env::set_var(name, value)); + let dir_wrapper = DirsWrapperMock::new() + .home_dir_result(Some(home_dir.clone())) + .data_dir_result(Some(data_dir.to_path_buf())); + let args = ArgsBuilder::new() + .param("--data-directory", current_directory.join(Path::new("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vlcs_properly/home")).to_string_lossy().to_string().as_str()) + .param("--real-user", "1001:1001:cooga"); + let args_vec: Vec = args.into(); + let params = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()); + let result = params.as_ref().expect("REASON"); + let multiconfig = result; + #[cfg(not(target_os = "windows"))] + { + assert_eq!( + value_m!(multiconfig, "config-file", String).unwrap(), + current_directory.join("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vlcs_properly/home/config.toml").to_string_lossy().to_string() + ); + assert_eq!(multiconfig.is_user_specified("--real-user"), true); + assert_eq!( + value_m!(multiconfig, "real-user", String).unwrap(), + "1001:1001:cooga".to_string() + ); + } + #[cfg(target_os = "windows")] + assert_eq!( + value_m!(multiconfig, "config-file", String).unwrap(), + current_directory.join("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vlcs_properly/home\\config.toml").to_string_lossy().to_string() + ); + assert_eq!(multiconfig.is_user_specified("--data-directory"), true); + } + #[test] fn server_initializer_collected_params_senses_when_user_specifies_config_file() { running_test(); @@ -879,6 +1326,7 @@ mod tests { #[test] fn server_initializer_collected_params_rejects_invalid_gas_price() { running_test(); + let _guard = EnvironmentGuard::new(); let _clap_guard = ClapGuard::new(); let args = ArgsBuilder::new().param("--gas-price", "unleaded"); let args_vec: Vec = args.into(); @@ -1055,18 +1503,19 @@ mod tests { .data_dir_result(Some(PathBuf::from(standard_data_dir))), }; - let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()) - .unwrap() - .data_directory - .to_string_lossy() - .to_string(); + let result = + server_initializer_collected_params(&dir_wrapper, args_vec.as_slice()).unwrap(); - assert_eq!(result, expected.unwrap()); + assert_eq!( + value_m!(result, "data-directory", String).unwrap(), + expected.unwrap() + ); } #[test] fn server_initializer_collected_params_senses_when_user_specifies_data_directory_without_chain_specific_directory( ) { + let _guard = EnvironmentGuard::new(); running_test(); let home_dir = Path::new("/home/cooga"); let home_dir_poly_main = home_dir.join(".local").join("MASQ").join("polygon-mainnet"); diff --git a/node/src/server_initializer.rs b/node/src/server_initializer.rs index 76a4c97d5..ece182c5d 100644 --- a/node/src/server_initializer.rs +++ b/node/src/server_initializer.rs @@ -8,6 +8,7 @@ use crate::node_configurator::{DirsWrapper, DirsWrapperReal}; use crate::run_modes_factories::{RunModeResult, ServerInitializer}; use crate::sub_lib::socket_server::ConfiguredByPrivilege; use backtrace::Backtrace; +use clap::value_t; use flexi_logger::{ Cleanup, Criterion, DeferredNow, Duplicate, LevelFilter, LogSpecBuilder, Logger, Naming, Record, }; @@ -17,7 +18,6 @@ use log::{log, Level}; use masq_lib::command::StdStreams; use masq_lib::logger; use masq_lib::logger::{real_format_function, POINTER_TO_FORMAT_FUNCTION}; -use masq_lib::multi_config::MultiConfig; use masq_lib::shared_schema::ConfiguratorError; use std::any::Any; use std::io; @@ -37,34 +37,34 @@ pub struct ServerInitializerReal { impl ServerInitializer for ServerInitializerReal { fn go(&mut self, streams: &mut StdStreams<'_>, args: &[String]) -> RunModeResult { let params = server_initializer_collected_params(self.dirs_wrapper.as_ref(), args)?; + let real_user = value_m!(params, "real-user", RealUser) + .expect("ServerInitializer: Real user not present in Multi Config"); + let data_directory = value_m!(params, "data-directory", String) + .expect("ServerInitializer: Data directory not present in Multi Config"); let result: RunModeResult = Ok(()) .combine_results( self.dns_socket_server .as_mut() - .initialize_as_privileged(¶ms.multi_config), + .initialize_as_privileged(¶ms), ) - .combine_results( - self.bootstrapper - .as_mut() - .initialize_as_privileged(¶ms.multi_config), - ); + .combine_results(self.bootstrapper.as_mut().initialize_as_privileged(¶ms)); self.privilege_dropper - .chown(¶ms.data_directory, ¶ms.real_user); + .chown(Path::new(data_directory.as_str()), &real_user); - self.privilege_dropper.drop_privileges(¶ms.real_user); + self.privilege_dropper.drop_privileges(&real_user); result .combine_results( self.dns_socket_server .as_mut() - .initialize_as_unprivileged(¶ms.multi_config, streams), + .initialize_as_unprivileged(¶ms, streams), ) .combine_results( self.bootstrapper .as_mut() - .initialize_as_unprivileged(¶ms.multi_config, streams), + .initialize_as_unprivileged(¶ms, streams), ) } implement_as_any!(); @@ -115,29 +115,6 @@ impl ResultsCombiner for RunModeResult { } } -pub struct GatheredParams<'a> { - pub multi_config: MultiConfig<'a>, - pub config_file_path: PathBuf, - pub real_user: RealUser, - pub data_directory: PathBuf, -} - -impl<'a> GatheredParams<'a> { - pub fn new( - multi_config: MultiConfig<'a>, - config_file_path: PathBuf, - real_user: RealUser, - data_directory: PathBuf, - ) -> Self { - Self { - multi_config, - config_file_path, - real_user, - data_directory, - } - } -} - lazy_static! { pub static ref LOGFILE_NAME: Mutex = Mutex::new(PathBuf::from("uninitialized")); } diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 3dfd8839e..f8bf58b02 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -573,7 +573,7 @@ pub mod unshared_test_utils { use masq_lib::utils::slice_of_strs_to_vec_of_strings; use std::any::TypeId; use std::cell::RefCell; - use std::collections::HashMap; + use std::collections::{HashMap, HashSet}; use std::num::ParseIntError; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::path::{Path, PathBuf}; @@ -643,7 +643,7 @@ pub mod unshared_test_utils { let mut app_args = vec!["MASQNode".to_string()]; app_args.append(&mut slice_of_strs_to_vec_of_strings(&args)); let arg_matches = app_node().get_matches_from_safe(app_args).unwrap(); - MultiConfig::new_test_only(arg_matches) + MultiConfig::new_test_only(arg_matches, HashSet::new()) } pub const ZERO: u32 = 0b0; diff --git a/node/tests/utils.rs b/node/tests/utils.rs index b60017063..bc80d7d39 100644 --- a/node/tests/utils.rs +++ b/node/tests/utils.rs @@ -273,6 +273,8 @@ impl MASQNode { #[allow(dead_code)] pub fn wait_for_exit(&mut self) -> Option { + //TODO Put the body of this function in a background thread and wait on the thread for a few + // seconds. If the thread doesn't terminate, leak the thread and return None. let child_opt = self.child.take(); let output_opt = self.output.take(); match (child_opt, output_opt) {