diff --git a/CHANGELOG.md b/CHANGELOG.md index e90d3fbd86ac45bf2b9042808a30e3ae02285d7a..e434767539f34e0256982a279902650012cd1e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,14 +25,15 @@ with the `YY.0M.MICRO` scheme. - System table `_pico_replicaset` now has a different format: the field `master_id` is replaced with 2 fields `current_master_id` and `target_master_id`. - - ### CLI - New command `picodata admin` to connect to picodata instance via unix socket under the admin account. - SQL by default in `picodata connect`. Lua language is deprecated in `picodata connect`. +- Rename `picodata run --console-sock` option to `--admin-sock` and + provide a default value `<data_dir>/admin.sock`. + ## [23.12.0] - 2023-12-08 ### Features diff --git a/src/cli/args.rs b/src/cli/args.rs index a7b260841c54e8044ef56f006adb8cb558f84bb8..a8b3d820c433cfef8aa90e439a8be3364868b344 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -135,12 +135,13 @@ pub struct Run { /// Enable interactive console pub interactive_mode: bool, - #[clap(long, value_name = "PATH", env = "PICODATA_CONSOLE_SOCK")] + #[clap(long, value_name = "PATH", env = "PICODATA_ADMIN_SOCK")] /// Unix socket for the interactive console to connect using - /// `picodata admin`. Unlike connecting to a - /// `--listen` address, console communication occurs in plain text + /// `picodata admin`. Unlike connecting via `picodata connect` + /// console communication occurs in plain text /// and always operates under the admin account. - pub console_sock: Option<String>, + /// Default value: <data_dir>/admin.sock + pub admin_sock: Option<String>, #[clap( long, @@ -220,6 +221,13 @@ impl Run { Ok(args) } + pub fn admin_sock(&self) -> String { + match &self.admin_sock { + Some(path) => path.clone(), + None => self.data_dir.clone() + "/admin.sock", + } + } + pub fn advertise_address(&self) -> String { let Address { host, port, .. } = self.advertise_address.as_ref().unwrap_or(&self.listen); format!("{host}:{port}") diff --git a/src/lib.rs b/src/lib.rs index 2a03c58e5f36ab8589bfb26fd19bb36fd2e93ad8..5057962c5e57c1108725b7ae35c2a208010ce57c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,6 @@ use ::tarantool::{fiber, session}; use rpc::{join, update_instance}; use std::cell::OnceCell; use std::collections::HashMap; -use std::io; use std::time::Duration; use storage::Clusterwide; use traft::RaftSpaceAccess; @@ -31,7 +30,7 @@ use crate::plugin::*; use crate::schema::ADMIN_ID; use crate::tier::{Tier, DEFAULT_TIER}; use crate::traft::op; -use crate::util::{effective_user_id, unwrap_or_terminate, validate_and_complete_unix_socket_path}; +use crate::util::{effective_user_id, listen_admin_console, unwrap_or_terminate}; mod access_control; pub mod audit; @@ -787,31 +786,7 @@ fn postjoin(args: &args::Run, storage: Clusterwide, raft_storage: RaftSpaceAcces box_cfg.listen = Some(format!("{}:{}", args.listen.host, args.listen.port)); tarantool::set_cfg(&box_cfg); - // Listen interactive console connections on a unix socket - if let Some(ref console_sock) = args.console_sock { - let l = ::tarantool::lua_state(); - - let validated_path = validate_and_complete_unix_socket_path(console_sock); - - if validated_path.is_err() { - tlog!( - Critical, - "failed to listen interactive console on {console_sock:?}: invalid path" - ); - std::process::exit(-1); - } - - let console_sock = validated_path.unwrap(); - if let Err(e) = l.exec_with(r#"require('console').listen(...)"#, &console_sock) { - tlog!(Error, "{e}"); - tlog!( - Critical, - "failed to listen interactive console on {console_sock:?}: {}", - io::Error::last_os_error() - ); - std::process::exit(-1); - } - } + unwrap_or_terminate(listen_admin_console(args)); if let Err(e) = tarantool::on_shutdown(move || fiber::block_on(on_shutdown::callback(PluginList::get()))) diff --git a/src/util.rs b/src/util.rs index 52f66df7e5b77da5c70c75f7ff43839ba035c808..5d0689d62023be5d834dbbef14610eb2f7c18116 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,4 @@ +use crate::cli::args; use crate::traft::error::Error; use nix::sys::termios::{tcgetattr, tcsetattr, LocalFlags, SetArg::TCSADRAIN}; use std::any::{Any, TypeId}; @@ -509,15 +510,16 @@ pub fn prompt_password(prompt: &str) -> Result<String, std::io::Error> { //////////////////////////////////////////////////////////////////////////////// /// Validate unix socket uri via lua uri module /// -/// Unix socket uri should start with ./ or ../ so we prepend it manually +/// Doesn't change path in case of absolute path. +/// To relative path `./` prepended. /// /// Return None in case of incorrect path -/// Return Some(`value`) with `unix/:` and, probably, `./`, `../` prepended to `value` +/// Return Some(`value`) with `unix/:` and, probably, `./` prepended to `value` pub fn validate_and_complete_unix_socket_path(socket_path: &str) -> Result<String, String> { let l = ::tarantool::lua_state(); let path = std::path::Path::new(socket_path); let console_sock = match path.components().next() { - Some(std::path::Component::Normal(_)) => { + Some(std::path::Component::Normal(_)) | Some(std::path::Component::ParentDir) => { format!("unix/:./{socket_path}") } _ => format!("unix/:{socket_path}"), @@ -533,6 +535,19 @@ pub fn validate_and_complete_unix_socket_path(socket_path: &str) -> Result<Strin Ok(console_sock) } +//////////////////////////////////////////////////////////////////////////////// +/// Starts admin console. +/// +/// Returns Err in case of problems with socket path. +pub fn listen_admin_console(args: &args::Run) -> Result<(), String> { + let lua = ::tarantool::lua_state(); + + let validated_path = validate_and_complete_unix_socket_path(&args.admin_sock())?; + + lua.exec_with(r#"require('console').listen(...)"#, &validated_path) + .map_err(|err| err.to_string()) +} + //////////////////////////////////////////////////////////////////////////////// /// Like unwrap(), but instead of a panic it logs /// the error in picodata format and calls exit() diff --git a/test/int/test_cli_connect.py b/test/int/test_cli_connect.py index 37f2a1f0743005151918969dfadfe318b8f46e46..5b3ab21dc36ea4d4aa278dd3f466018c9001d345 100644 --- a/test/int/test_cli_connect.py +++ b/test/int/test_cli_connect.py @@ -233,30 +233,29 @@ def test_admin_empty_path(binary_path: str): cli.expect_exact(pexpect.EOF) -def test_admin_ok(cluster: Cluster): +def test_connect_unix_ok_via_default_sock(cluster: Cluster): i1 = cluster.add_instance(wait_online=False) - i1.env.update({"PICODATA_CONSOLE_SOCK": f"{i1.data_dir}/console.sock"}) i1.start() i1.wait_online() cli = pexpect.spawn( # For some uninvestigated reason, readline trims the propmt in CI # Instead of - # unix/:/some/path/to/console.sock> + # unix/:/some/path/to/admin.sock> # it prints - # </path/to/console.sock> + # </path/to/admin.sock> # # We were unable to debug it quickly and used cwd as a workaround cwd=i1.data_dir, command=i1.binary_path, - args=["admin", "./console.sock"], + args=["admin", "./admin.sock"], encoding="utf-8", timeout=1, ) cli.logfile = sys.stdout - cli.expect_exact("connected to unix/:./console.sock") - cli.expect_exact("unix/:./console.sock>") + cli.expect_exact("connected to unix/:./admin.sock") + cli.expect_exact("unix/:./admin.sock>") cli.sendline("\\set language lua") cli.sendline("box.session.user()")