Skip to content
Snippets Groups Projects
Commit 49eecfa1 authored by Yaroslav Dynnikov's avatar Yaroslav Dynnikov
Browse files

Parse command line arguments

The control flow of picodata looks as following:

Picodata binary -> exec tarantool -l picodata -> libpicodata.so -> ...

Since the libpicodata is a shared lib loaded by tarantool, we can't pass
it arguments as usual. At first, tarantool parses them by itself and
rejects uknown arguments. Secondly, it spoils argv array.

To overcome these two issues, this patch introduces argparse on behalf
of the picodata binary, and puts parsed values into the environment
variables.

P.S. Thihs is how command line args look from within libpicodata:

```console
$ tarantool -l picodata /dev/null --opt1 foo --opt2 bar
Hello from rust lib
arg[0] tarantool
arg[1] /dev/null
arg[2] --opt1
arg[3] foo
arg[4] --opt2
arg[5] bar
arg[6] --opt2
arg[7] bar
```
parent 9223e526
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = {version = "2.33", features = ["yaml"]}
libc = "0.2.108"
errno = "0.2.8"
......
name: picodata
version: "0.1"
args: []
subcommands:
- run:
about: run the picodata instance
args:
- listen:
# short: l
long: listen
help: Socket bind address
takes_value: true
default_value: "0.0.0.0:3301"
value_name: "[host:]port"
- peer:
# short: p
long: peer
help: Address of another instance(s)
multiple: true
takes_value: true
value_name: "host:port"
- cluster-id:
long: cluster-id
help: Name of the cluster
value_name: "name"
takes_value: true
- instance-id:
long: instance-id
help: Name of the instance
value_name: "name"
takes_value: true
- replicaset-id:
long: replicaset-id
help: Name of the replicaset
value_name: "name"
takes_value: true
- tarantool-exec:
short: e
help: Execute tarantool script
value_name: expr
# - advertise-peer:
# help: URI advertised to other instances in cluster
# long: advertise
......@@ -3,5 +3,10 @@ use std::os::raw::c_int;
#[no_mangle]
pub extern "C" fn luaopen_picodata(_l: std::ffi::c_void) -> c_int {
println!("Hello from rust lib");
for (key, value) in std::env::vars() {
if key.starts_with("PICODATA_") {
println!("{}: {}", key, value);
}
}
0
}
use clap::App;
use std::collections::HashMap;
use std::ffi::CString;
#[macro_use]
extern crate clap;
mod execve;
macro_rules! CString {
......@@ -10,6 +14,23 @@ macro_rules! CString {
}
fn main() {
let yml = load_yaml!("cli.yml");
let matches = App::from_yaml(yml).version(crate_version!()).get_matches();
println!("{:?}", matches);
if matches.subcommand_name().is_none() {
println!("{}", matches.usage());
std::process::exit(0);
}
if let Some(matches) = matches.subcommand_matches("run") {
return main_run(matches);
}
unreachable!();
}
fn main_run(matches: &clap::ArgMatches) {
let tarantool_path: String = match execve::which("tarantool") {
Some(v) => v
.into_os_string()
......@@ -21,11 +42,12 @@ fn main() {
}
};
let args = [&tarantool_path, "-l", "picodata"];
let argv = std::iter::empty()
.chain(args.iter().map(|&s| CString!(s)))
.chain(std::env::args().skip(1).map(|s| CString!(s)))
.collect();
let mut args: Vec<&str> = vec![&tarantool_path, "-l", "picodata"];
if let Some(script) = matches.value_of("tarantool-exec") {
args.push("-e");
args.push(script);
}
let argv = args.iter().map(|&s| CString!(s)).collect();
let mut envp = HashMap::new();
for (k, v) in std::env::vars() {
......@@ -34,6 +56,19 @@ fn main() {
}
}
if let Some(peer) = matches.values_of("peer") {
let append = |s: String, str| if s.is_empty() {s + str} else {s + "," + str};
let peer = peer.fold(String::new(), append);
envp.insert("PICODATA_PEER".to_owned(), peer);
}
for arg in ["listen", "instance-id", "replicaset-id", "cluster-id"] {
if let Some(v) = matches.value_of(arg) {
let k = format!("PICODATA_{}", arg.to_uppercase().replace("-", "_"));
envp.insert(k, v.to_owned());
}
}
// In order to make libpicodata loaded we should provide
// its path in the LUA_CPATH env variable.
let cpath: String = {
......
......@@ -5,12 +5,14 @@ use tempfile::tempdir;
#[test]
fn positive() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.arg("run");
cmd.assert().success();
}
#[test]
fn missing_tarantool() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.arg("run");
cmd.env("PATH", "/nowhere");
cmd.assert()
.failure()
......@@ -24,6 +26,7 @@ fn broken_tarantool() {
File::create(temp_path.join("tarantool")).unwrap();
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.arg("run");
cmd.env("PATH", temp_path);
cmd.assert().failure().stderr(format!(
"{}/tarantool: {}\n",
......@@ -35,6 +38,7 @@ fn broken_tarantool() {
#[test]
fn pass_arguments() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.arg("run");
cmd.args(["-e", "error('xxx', 0)"]);
cmd.assert().failure().stderr(
"LuajitError: xxx\n\
......@@ -45,10 +49,17 @@ fn pass_arguments() {
#[test]
fn pass_environment() {
let mut cmd = Command::cargo_bin("picodata").unwrap();
cmd.arg("run");
cmd.env("LUA_CPATH", "/dev/null/?");
cmd.env("CUSTOM_VAR", "keep me");
cmd.env("TARANTOOL_VAR", "purge me");
cmd.env("TT_VAR", "purge me too");
cmd.args(["--listen", "127.0.0.1:3301"]);
cmd.args(["--cluster-id", "sam"]);
cmd.args(["--replicaset-id", "r1"]);
cmd.args(["--instance-id", "i1"]);
cmd.args(["--peer", "i1,i2"]);
cmd.args(["--peer", "i3"]);
cmd.arg("-e").arg(
r#"
cpath = os.environ()['LUA_CPATH']
......@@ -62,6 +73,11 @@ fn pass_environment() {
assert_eq(os.environ()['CUSTOM_VAR'], 'keep me')
assert_eq(os.environ()['TARANTOOL_VAR'], nil)
assert_eq(os.environ()['TT_VAR'], nil)
assert_eq(os.environ()['PICODATA_LISTEN'], "127.0.0.1:3301")
assert_eq(os.environ()['PICODATA_CLUSTER_ID'], "sam")
assert_eq(os.environ()['PICODATA_REPLICASET_ID'], "r1")
assert_eq(os.environ()['PICODATA_INSTANCE_ID'], "i1")
assert_eq(os.environ()['PICODATA_PEER'], "i1,i2,i3")
"#,
);
cmd.assert().success();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment