From baaa36d336aae1d2a79960f455471e0b38267bcb Mon Sep 17 00:00:00 2001 From: Sayan Nandan Date: Sat, 24 Feb 2024 10:59:17 +0530 Subject: [PATCH] Improve help message generation and allow env var for password --- Cargo.lock | 3 ++ cli/Cargo.toml | 4 +++ cli/build.rs | 3 ++ cli/help_text/help | 9 +++--- cli/src/args.rs | 14 ++++++--- libsky/Cargo.toml | 1 + libsky/src/lib.rs | 57 ++++++++++++++++++++++++++++++++++ server/Cargo.toml | 4 +++ server/build.rs | 3 ++ server/help_text/help | 40 ++++++++++++++++++++++++ server/src/engine/config.rs | 36 ++------------------- server/src/engine/mod.rs | 18 +++-------- server/src/engine/tests/cfg.rs | 2 +- server/src/main.rs | 28 ++++++++++------- server/src/util/macros.rs | 19 ++++++++++++ sky-bench/Cargo.toml | 5 ++- sky-bench/build.rs | 3 ++ sky-bench/help_text/help | 6 ++-- sky-bench/src/args.rs | 18 +++++++---- 19 files changed, 197 insertions(+), 76 deletions(-) create mode 100644 cli/build.rs create mode 100644 server/build.rs create mode 100644 server/help_text/help create mode 100644 sky-bench/build.rs diff --git a/Cargo.lock b/Cargo.lock index 51ba0c34..0cab13a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -720,6 +720,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libsky" version = "0.8.0-beta.4" +dependencies = [ + "regex", +] [[package]] name = "linux-raw-sys" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ddee5b66..81b56f05 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -4,9 +4,13 @@ version = "0.8.0-beta.4" authors = ["Sayan Nandan "] edition = "2021" description = "The Skytable Shell (skysh)" +build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[build-dependencies] +libsky = { path = "../libsky" } + [dependencies] # internal deps libsky = { path = "../libsky" } diff --git a/cli/build.rs b/cli/build.rs new file mode 100644 index 00000000..01b95251 --- /dev/null +++ b/cli/build.rs @@ -0,0 +1,3 @@ +fn main() -> std::io::Result<()> { + libsky::build_scripts::format_help_txt("skysh", "help_text/help", Default::default()) +} diff --git a/cli/help_text/help b/cli/help_text/help index 736b376c..4c00b0b1 100644 --- a/cli/help_text/help +++ b/cli/help_text/help @@ -1,4 +1,4 @@ -skysh 0.8.0-beta.4 +skysh {version} Sayan N. The Skytable interactive shell (skysh) @@ -16,14 +16,15 @@ OPTIONS: --tls-cert Set the TLS certificate to use (for TLS endpoints) NOTES: + - skysh will also look for the `{password_env_var}` environment variable - When no endpoint is specified, skysh will attempt to connect to the default - TCP endpoint `tcp@127.0.0.1:2003` + TCP endpoint `{default_tcp_endpoint}` - When no user is specified, skysh will attempt to authenticate as root - All connections need an username and password. If this is not provided via arguments, it will be asked for interactively - Endpoints are specified using the Skytable endpoint syntax. For example, - the default TCP endpoint is `tcp@127.0.0.1:2003` while the default TLS - endpoint is `tls@127.0.0.1:2004` + the default TCP endpoint is `{default_tcp_endpoint}` while the default TLS + endpoint is `{default_tls_endpoint}` - If you choose to use a TLS endpoint, you must provide a certificate. Failing to do so will throw an error, as expected - All history is stored in the `.sky_history` file. If you wish to delete diff --git a/cli/src/args.rs b/cli/src/args.rs index 3a84735a..1e799866 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -30,16 +30,16 @@ use { event::{self, Event, KeyCode, KeyEvent}, terminal, }, - libsky::CliAction, + libsky::{env_vars, CliAction}, std::{ collections::HashMap, - fs, + env, fs, io::{self, Write}, process::exit, }, }; -const TXT_HELP: &str = include_str!("../help_text/help"); +const TXT_HELP: &str = include_str!(concat!(env!("OUT_DIR"), "/skysh")); #[derive(Debug)] pub struct ClientConfig { @@ -148,7 +148,13 @@ pub fn parse() -> CliResult { }; let password = match args.remove("--password") { Some(p) => p, - None => read_password("Enter password: ")?, + None => { + // let us check the environment variable to see if anything was set + match env::var(env_vars::SKYDB_PASSWORD) { + Ok(v) => v, + Err(_) => read_password("Enter password: ")?, + } + } }; if args.is_empty() { Ok(Task::OpenShell(ClientConfig::new( diff --git a/libsky/Cargo.toml b/libsky/Cargo.toml index df829511..cf9d074b 100644 --- a/libsky/Cargo.toml +++ b/libsky/Cargo.toml @@ -7,3 +7,4 @@ version = "0.8.0-beta.4" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1" diff --git a/libsky/src/lib.rs b/libsky/src/lib.rs index 0532bc3a..d60f91a4 100644 --- a/libsky/src/lib.rs +++ b/libsky/src/lib.rs @@ -36,6 +36,11 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// The URL pub const URL: &str = "https://github.com/skytable/skytable"; +pub mod env_vars { + /// the environment variable to set the password to use with any tool (skysh,sky-bench,..) + pub const SKYDB_PASSWORD: &str = "SKYDB_PASSWORD"; +} + pub mod test_utils { pub const DEFAULT_USER_NAME: &str = "root"; pub const DEFAULT_USER_PASS: &str = "mypassword12345678"; @@ -170,3 +175,55 @@ fn extract_arg( }; Ok((arg, value)) } + +/* + formatting utils +*/ + +pub fn format(body: &str, arguments: HashMap<&'static str, &'static str>, auto: bool) -> String { + use regex::Regex; + let pattern = r"\{[a-zA-Z_][a-zA-Z_0-9]*\}|\{\}"; + let re = Regex::new(pattern).unwrap(); + re.replace_all(body, |caps: ®ex::Captures| { + let capture: &str = &caps[0]; + let capture = &capture[1..capture.len() - 1]; + match capture { + "" => { + panic!("found an empty format") + } + "default_tcp_endpoint" if auto => "tcp@127.0.0.1:2003".to_owned(), + "default_tls_endpoint" if auto => "tls@127.0.0.1:2004".to_owned(), + "password_env_var" if auto => env_vars::SKYDB_PASSWORD.into(), + "version" if auto => format!("v{VERSION}"), + arbitrary => arguments + .get(arbitrary) + .expect(&format!("could not find value for argument {}", arbitrary)) + .to_string(), + } + }) + .to_string() +} + +pub mod build_scripts { + use std::{ + collections::HashMap, + env, + fs::{self, File}, + io::{self, Write}, + path::Path, + }; + pub fn format_help_txt( + binary_name: &str, + help_text_path: &str, + arguments: HashMap<&'static str, &'static str>, + ) -> io::Result<()> { + let help_msg = fs::read_to_string(help_text_path)?; + let content = super::format(&help_msg, arguments, true); + // write + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join(binary_name); + let mut f = File::create(&dest_path)?; + f.write_all(content.as_bytes())?; + Ok(()) + } +} diff --git a/server/Cargo.toml b/server/Cargo.toml index d9c8d4fc..223b4fc3 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -5,6 +5,10 @@ name = "skyd" version = "0.8.0-beta.4" description = "Skytable is a modern NoSQL database powered by BlueQL that aims to deliver performance, scalability and flexibility with data" license = "AGPL-3.0" +build = "build.rs" + +[build-dependencies] +libsky = { path = "../libsky" } [dependencies] # internal deps diff --git a/server/build.rs b/server/build.rs new file mode 100644 index 00000000..29ad9283 --- /dev/null +++ b/server/build.rs @@ -0,0 +1,3 @@ +fn main() -> std::io::Result<()> { + libsky::build_scripts::format_help_txt("skyd", "help_text/help", Default::default()) +} diff --git a/server/help_text/help b/server/help_text/help new file mode 100644 index 00000000..e1a54e75 --- /dev/null +++ b/server/help_text/help @@ -0,0 +1,40 @@ +███████ ██  ██ ██  ██ ████████  █████  ██████  ██  ███████ +██      ██  ██   ██  ██     ██    ██   ██ ██   ██ ██  ██ +███████ █████    ████   ██  ███████ ██████  ██  █████ +     ██ ██  ██   ██   ██  ██   ██ ██   ██ ██  ██ +███████ ██  ██  ██  ██  ██  ██ ██████  ███████ ███████ + +Skytable {version} | https://github.com/skytable/skytable + +Sayan N. +Skytable database server + +Usage: skyd [OPTION]... + +skyd is the Skytable database server daemon and can be used to serve database requests. + +Flags: + -h, --help Display this help menu and exit. + -v, --version Display the version number and exit. + +Options: + --config Set configuration options using the config file + --tlscert Specify the path to the TLS certificate. + --tlskey Specify the path to the TLS private key. + --endpoint Designate an endpoint. Format: protocol@host:port. + This option can be repeated to define multiple endpoints. + --service-window Set the time window for the background service in seconds. + --auth Identify the authentication plugin by name. + --mode Set the operational mode. Note: This option is mandatory. + --auth-plugin Set the auth plugin. `pwd` is a supported option + --auth-root-password Set the root password + +Examples: + skyd --auth-root-password "password12345678" + +Notes: + - If no `--mode` is provided, we default to `dev` + - You must provide `--auth-root-password` to set the default root password + - To use TLS, you must provide both `--tlscert` and `--tlskey` + +For further assistance, refer to the official documentation here: https://docs.skytable.org \ No newline at end of file diff --git a/server/src/engine/config.rs b/server/src/engine/config.rs index 90779dc3..cef98899 100644 --- a/server/src/engine/config.rs +++ b/server/src/engine/config.rs @@ -333,7 +333,7 @@ impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.source { Some(src) => write!(f, "config error in {}: ", src.as_str())?, - None => write!(f, "config error: ")?, + None => {} } match &self.kind { ConfigErrorKind::Conflict => write!( @@ -640,37 +640,7 @@ fn arg_decode_rs_window( */ /// CLI help message -pub(super) const CLI_HELP: &str = "\ -Usage: skyd [OPTION]... - -skyd is the Skytable database server daemon and can be used to serve database requests. - -Flags: - -h, --help Display this help menu and exit. - -v, --version Display the version number and exit. - -Options: - --config Set configuration options using the config file - --tlscert Specify the path to the TLS certificate. - --tlskey Specify the path to the TLS private key. - --endpoint Designate an endpoint. Format: protocol@host:port. - This option can be repeated to define multiple endpoints. - --service-window Establish the time window for the background service in seconds. - --auth Identify the authentication plugin by name. - --mode Set the operational mode. Note: This option is mandatory. - --auth-plugin Set the auth plugin. `pwd` is a supported option - --auth-root-password Set the root password - -Examples: - skyd --mode=dev --auth-root-password \"password12345678\" - -Notes: - - If no `--mode` is provided, we default to `dev` - - You must provide `--auth-root-password` to set the default root password - - To use TLS, you must provide both `--tlscert` and `--tlskey` - -For further assistance, refer to the official documentation here: https://docs.skytable.org -"; +pub(super) const TXT_HELP: &str = include_str!(concat!(env!("OUT_DIR"), "/skyd")); #[derive(Debug, PartialEq)] /// Return from parsing CLI configuration @@ -1128,7 +1098,7 @@ pub fn check_configuration() -> RuntimeResult { // no options were provided in the CLI None } - CLIConfigParseReturn::Help => return Ok(ConfigReturn::HelpMessage(CLI_HELP.into())), + CLIConfigParseReturn::Help => return Ok(ConfigReturn::HelpMessage(TXT_HELP.into())), CLIConfigParseReturn::Version => { // just output the version return Ok(ConfigReturn::HelpMessage(format!( diff --git a/server/src/engine/mod.rs b/server/src/engine/mod.rs index 08a229fc..6a2b2cc3 100644 --- a/server/src/engine/mod.rs +++ b/server/src/engine/mod.rs @@ -26,7 +26,7 @@ #[macro_use] mod macros; -mod config; +pub mod config; mod core; mod data; mod error; @@ -48,11 +48,10 @@ use crate::engine::storage::SELoaded; use { self::{ - config::{ConfigEndpoint, ConfigEndpointTls, ConfigMode, ConfigReturn, Configuration}, + config::{ConfigEndpoint, ConfigEndpointTls, ConfigMode, Configuration}, fractal::context::{self, Subsystem}, }, crate::util::os::TerminationSignal, - std::process::exit, tokio::sync::broadcast, }; @@ -63,17 +62,10 @@ pub(super) fn set_context_init(msg: &'static str) { /// Initialize all drivers, load all data /// /// WARN: Must be in [`tokio::runtime::Runtime`] context! -pub fn load_all() -> RuntimeResult<(Configuration, fractal::GlobalStateStart)> { +pub fn load_all( + config: Configuration, +) -> RuntimeResult<(Configuration, fractal::GlobalStateStart)> { // load configuration - info!("checking configuration ..."); - context::set(Subsystem::Init, "loading configuration"); - let config = match config::check_configuration()? { - ConfigReturn::Config(cfg) => cfg, - ConfigReturn::HelpMessage(msg) => { - eprintln!("{msg}"); - exit(0x00); - } - }; if config.mode == ConfigMode::Dev { warn!("running in dev mode"); } diff --git a/server/src/engine/tests/cfg.rs b/server/src/engine/tests/cfg.rs index dd8e2935..ed0cc66f 100644 --- a/server/src/engine/tests/cfg.rs +++ b/server/src/engine/tests/cfg.rs @@ -144,7 +144,7 @@ fn parse_validate_cli_args_help_and_version() { let ret4 = config::check_configuration().unwrap(); assert_eq!( ret3, - ConfigReturn::HelpMessage(config::CLI_HELP.to_string()) + ConfigReturn::HelpMessage(config::TXT_HELP.to_string()) ); assert_eq!( ret4, diff --git a/server/src/main.rs b/server/src/main.rs index 7c4188be..252f5f6f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -42,10 +42,7 @@ extern crate log; pub mod util; mod engine; -use { - crate::util::exit_error, - libsky::{URL, VERSION}, -}; +use libsky::{URL, VERSION}; #[cfg(all(not(target_env = "msvc"), not(miri)))] #[global_allocator] @@ -65,14 +62,24 @@ type IoResult = std::io::Result; const SKY_PID_FILE: &str = ".sky_pid"; fn main() { + use crate::engine::config::ConfigReturn; Builder::new() .parse_filters(&env::var("SKY_LOG").unwrap_or_else(|_| "info".to_owned())) .init(); - println!("{TEXT}\nSkytable v{VERSION} | {URL}\n"); - entrypoint() + let config = match engine::config::check_configuration() { + Ok(cfg) => match cfg { + ConfigReturn::Config(cfg) => cfg, + ConfigReturn::HelpMessage(msg) => { + exit!(eprintln!("{msg}"), 0x00) + } + }, + Err(e) => exit_fatal!(error!("{e}")), + }; + self::entrypoint(config) } -fn entrypoint() { +fn entrypoint(config: engine::config::Configuration) { + println!("{TEXT}\nSkytable v{VERSION} | {URL}\n"); let run = || { let f_rt_start = || { engine::set_context_init("locking PID file"); @@ -91,7 +98,7 @@ fn entrypoint() { let f_glob_init = runtime.block_on(async move { engine::set_context_init("binding system signals"); let signal = util::os::TerminationSignal::init()?; - let (config, global) = tokio::task::spawn_blocking(|| engine::load_all()) + let (config, global) = tokio::task::spawn_blocking(|| engine::load_all(config)) .await .unwrap()?; engine::RuntimeResult::Ok((signal, config, global)) @@ -117,9 +124,6 @@ fn entrypoint() { } match result { Ok(()) => println!("goodbye"), - Err(e) => { - error!("{e}"); - exit_error(); - } + Err(e) => exit_fatal!(error!("{e}")), } } diff --git a/server/src/util/macros.rs b/server/src/util/macros.rs index 8c2849e3..cc8b014a 100644 --- a/server/src/util/macros.rs +++ b/server/src/util/macros.rs @@ -318,3 +318,22 @@ macro_rules! concat_str_to_str { concat_str_to_str!(A, $c) }}; } + +#[macro_export] +macro_rules! exit { + ($do_it:expr, $code:expr) => {{ + $do_it; + ::std::process::exit($code) + }}; + ($code:expr) => { + ::std::process::exit($code) + }; +} + +#[macro_export] +macro_rules! exit_fatal { + ($do_it:expr) => {{ + $do_it; + $crate::util::exit_error() + }}; +} diff --git a/sky-bench/Cargo.toml b/sky-bench/Cargo.toml index 8d779c44..3c35c4df 100644 --- a/sky-bench/Cargo.toml +++ b/sky-bench/Cargo.toml @@ -4,9 +4,12 @@ edition = "2021" name = "sky-bench" version = "0.8.0-beta.4" description = "The Skytable benchmark tool can be used to benchmark Skytable installations" - +build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[build-dependencies] +libsky = { path = "../libsky" } + [dependencies] # internal deps skytable = { git = "https://github.com/skytable/client-rust.git" } diff --git a/sky-bench/build.rs b/sky-bench/build.rs new file mode 100644 index 00000000..9858aeb9 --- /dev/null +++ b/sky-bench/build.rs @@ -0,0 +1,3 @@ +fn main() -> std::io::Result<()> { + libsky::build_scripts::format_help_txt("sky-bench", "help_text/help", Default::default()) +} diff --git a/sky-bench/help_text/help b/sky-bench/help_text/help index 5c1ed876..fb15a09a 100644 --- a/sky-bench/help_text/help +++ b/sky-bench/help_text/help @@ -1,4 +1,4 @@ -sky-bench 0.8.0-beta.4 +sky-bench {version} Sayan N. Skytable benchmark tool @@ -13,7 +13,7 @@ REQUIRED OPTIONS: --password Provide the password OPTIONS: - --endpoint Set the endpoint (defaults to tcp@127.0.0.1:2003) + --endpoint Set the endpoint (defaults to {default_tcp_endpoint}) --threads Set the number of threads to be used (defaults to logical CPU count) --connections Set the number of connections. Defaults to 8 x logical CPU @@ -25,6 +25,8 @@ OPTIONS: and `fury` is the new experimental engine. Defaults to `fury` NOTES: + - If no password is supplied, we look for the `{password_env_var}` + environment variable - The user for auth will be 'root' since only 'root' accounts allow the creation and deletion of spaces and models - A space called 'bench' will be created diff --git a/sky-bench/src/args.rs b/sky-bench/src/args.rs index 2aa5336d..7526ed8e 100644 --- a/sky-bench/src/args.rs +++ b/sky-bench/src/args.rs @@ -26,11 +26,11 @@ use { crate::error::{BenchError, BenchResult}, - libsky::CliAction, - std::collections::hash_map::HashMap, + libsky::{env_vars, CliAction}, + std::{collections::hash_map::HashMap, env}, }; -const TXT_HELP: &str = include_str!("../help_text/help"); +const TXT_HELP: &str = include_str!(concat!(env!("OUT_DIR"), "/sky-bench")); #[derive(Debug)] enum TaskInner { @@ -144,9 +144,15 @@ pub fn parse() -> BenchResult { let passsword = match args.remove("--password") { Some(p) => p, None => { - return Err(BenchError::ArgsErr( - "you must provide a value for `--password`".into(), - )) + // check env? + match env::var(env_vars::SKYDB_PASSWORD) { + Ok(p) => p, + Err(_) => { + return Err(BenchError::ArgsErr( + "you must provide a value for `--password`".into(), + )) + } + } } }; // threads