Unverified Commit 7ee86aea authored by Vasili Novikov's avatar Vasili Novikov
Browse files

Design CLI parser+description

parent b38f071b
Showing with 212 additions and 30 deletions
+212 -30
......@@ -18,6 +18,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "async-trait"
version = "0.1.36"
......@@ -192,9 +201,13 @@ version = "2.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
......@@ -551,6 +564,15 @@ dependencies = [
"http",
]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.15"
......@@ -993,6 +1015,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
"structopt",
"tokio",
"warp",
]
......@@ -1012,6 +1035,30 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.19"
......@@ -1436,6 +1483,36 @@ dependencies = [
"generic-array 0.14.3",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "subtle"
version = "2.2.3"
......@@ -1665,6 +1742,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.8"
......@@ -1716,6 +1799,12 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"
......
......@@ -29,6 +29,7 @@ rusqlite = { version = "0.23.1", features = ["sqlcipher"] }
serde = { version = "1.0.114", features = ["derive"] }
serde_json = "1.0.57"
sha2 = "0.9.1"
structopt = { version = "0.3.15", features = ["color"] }
tokio = { version = "0.2.22", features = ["macros"] }
warp = { version = "0.2.4", default-features = false, features = ["tls"] }
......
use lazy_static::lazy_static;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::path::PathBuf;
use structopt::clap::AppSettings;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(
name = "Pod, the open-source backend for Memri project.",
setting = AppSettings::DeriveDisplayOrder,
setting = AppSettings::UnifiedHelpMessage,
version = VERSION.as_ref(),
)]
pub struct CLIOptions {
/// Port to listen to.
#[structopt(short, long, default_value = "3030")]
port: i16,
/// File to read https public certificate from.
#[structopt(
short = "c",
long,
default_value = "./data/certs/pod.crt",
name = "CERTIFICATE_FILE"
)]
https_pub_crt: String,
/// File to read https private key from.
#[structopt(
short = "k",
long,
default_value = "./data/certs/pod.key",
name = "KEY_FILE"
)]
https_priv_key: String,
/// Set the callback address for services launched from within Pod.
/// This should be the Pod-s address as seen by external services.
/// During development, it defaults to "https://pod_pod_1:3030" if Pod is inside docker,
/// or "https://localhost:3030" on Linux,
/// or "https://host.docker.internal:3030" on other operating systems.
#[structopt(short = "s", long, name = "ADDRESS")]
services_callback_address: Option<SocketAddr>,
/// Do not use https when starting the server, instead run on http://127.0.0.1.
/// Running on loopback interface (127.0.0.1) means that only apps
/// from within the same computer will be able to access Pod.
/// This option might be used during development as an alternative to self-signed certificates.
#[structopt(short = "t", long)]
non_tls: bool,
/// Unsafe version of --non-tls. Do not use https when starting the server,
/// instead run on http on the provided network interface.
/// WARNING: This is heavily discouraged as an intermediary
/// (even your router on a local network)
/// could spoof the traffic sent to the server and do a MiTM attack.
/// Please consider running Pod on a non-public network (--non-tls),
/// or use Pod with https encryption.
#[structopt(short, long)]
insecure_non_tls: Option<IpAddr>,
/// Comma-separated list of Pod owners (hex-encoded hashes of public keys).
/// Only those owners are allowed to call Pod endpoints.
/// Each Pod owner has its own database and files directory,
/// the owners do not intersect data-wise.
///
/// A magic value of "ANY" will allow any owner to connect to the Pod.
///
/// Can be specified multiple times.
#[structopt(short = "o", long, required = true, multiple = true)]
owner: Vec<String>,
/// Validate a schema file, and exit.
/// This allows testing whether a given schema is suitable for use in Pod,
/// see README.md#schema for details.
/// Note that running project tests or starting the Pod will also automatically validate
/// the schema in `res/autogenerated_database_schema.json`.
#[structopt(short = "v", long, name = "SCHEMA_FILE", parse(from_os_str))]
validate_schema: Option<PathBuf>,
}
lazy_static! {
// Don't change to `&'static str` for now, it's a bit hard to get lifetimes straight with Clap.
pub static ref VERSION: String = {
// Ideas for future:
// * print "which branch" does the commit belong to
// * print "dirty" indicator (whether there are local uncommitted changes)
env!("GIT_DESCRIBE").to_string()
};
}
// Not a real configuration, but one place that lists all environment variable with their usages.
// Constants used in the project. These are "convention over configuration" for now.
use std::env;
......
use crate::configuration;
use crate::constants;
use crate::error::Error;
use crate::error::Result;
use chacha20poly1305::aead::Aead;
......@@ -159,11 +159,8 @@ fn find_key_and_nonce_by_sha256(tx: &Transaction, sha256: &str) -> Result<(Vec<u
}
fn media_dir() -> Result<PathBuf> {
PathBuf::from_str(configuration::MEDIA_DIR).map_err(|err| {
warn!(
"Failed to create file upload path {}",
configuration::MEDIA_DIR
);
PathBuf::from_str(constants::MEDIA_DIR).map_err(|err| {
warn!("Failed to create file upload path {}", constants::MEDIA_DIR);
Error {
code: StatusCode::INTERNAL_SERVER_ERROR,
msg: format!("Failed to create file upload path, {}", err),
......
......@@ -22,9 +22,7 @@ use std::str;
use warp::http::status::StatusCode;
pub fn get_project_version() -> String {
let git = env!("GIT_DESCRIBE");
let cargo = env!("CARGO_PKG_VERSION");
format!("{}-cargo{})", git, cargo)
crate::command_line_interface::VERSION.to_string()
}
pub fn get_item(conn: &Connection, uid: i64) -> Result<Vec<Value>> {
......
// Fake simple library interface to allow integration tests to work
mod api_model;
pub mod configuration;
mod command_line_interface;
pub mod constants;
pub mod database_migrate_refinery;
pub mod database_migrate_schema;
pub mod error;
......
......@@ -3,7 +3,8 @@ extern crate r2d2_sqlite;
extern crate rusqlite;
mod api_model;
mod configuration;
mod command_line_interface;
mod constants;
mod database_migrate_refinery;
pub mod database_migrate_schema;
mod error;
......@@ -22,6 +23,7 @@ use std::fs::create_dir_all;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use structopt::StructOpt;
#[tokio::main]
async fn main() {
......@@ -41,13 +43,16 @@ async fn main() {
std::env!("GIT_DESCRIBE"),
std::env!("CARGO_PKG_VERSION")
);
let opt = command_line_interface::CLIOptions::from_args();
println!("{:#?}", opt);
if std::env::args().any(|a| a == "--version" || a == "--help") {
eprintln!("Done");
std::process::exit(0)
};
create_config_directory(configuration::DATABASE_DIR);
create_config_directory(configuration::MEDIA_DIR);
create_config_directory(constants::DATABASE_DIR);
create_config_directory(constants::MEDIA_DIR);
// Start web framework
warp_api::run_server().await;
......
use crate::api_model::RunDownloader;
use crate::api_model::RunImporter;
use crate::api_model::RunIndexer;
use crate::configuration::pod_is_in_docker;
use crate::constants::pod_is_in_docker;
use crate::error::Error;
use crate::error::Result;
use crate::internal_api;
......@@ -125,9 +125,9 @@ pub fn run_indexers(conn: &Connection, payload: RunIndexer) -> Result<()> {
}
fn docker_arguments() -> Vec<String> {
let is_https = crate::configuration::https_certificate_file().is_some();
let is_https = crate::constants::https_certificate_file().is_some();
let schema = if is_https { "https" } else { "http" };
let port = crate::configuration::DEFAULT_PORT;
let port = crate::constants::DEFAULT_PORT;
if pod_is_in_docker() {
vec![
"--network=pod_memri-net".to_string(),
......
......@@ -6,7 +6,7 @@ use crate::api_model::RunDownloader;
use crate::api_model::RunImporter;
use crate::api_model::RunIndexer;
use crate::api_model::UpdateItem;
use crate::configuration;
use crate::constants;
use crate::internal_api;
use crate::warp_endpoints;
use bytes::Bytes;
......@@ -243,9 +243,9 @@ pub async fn run_server() {
.or(get_file.with(&headers))
.or(origin_request);
if let Some(cert) = configuration::https_certificate_file() {
let addr = configuration::pod_listen_address()
.unwrap_or_else(|| format!("0.0.0.0:{}", configuration::DEFAULT_PORT));
if let Some(cert) = constants::https_certificate_file() {
let addr = constants::pod_listen_address()
.unwrap_or_else(|| format!("0.0.0.0:{}", constants::DEFAULT_PORT));
let addr = SocketAddr::from_str(&addr).unwrap_or_else(|err| {
error!("Failed to parse desired hosting address {}, {}", addr, err);
std::process::exit(1)
......@@ -273,8 +273,8 @@ pub async fn run_server() {
.run(addr)
.await;
} else {
let addr = configuration::pod_listen_address()
.unwrap_or_else(|| format!("127.0.0.1:{}", configuration::DEFAULT_PORT));
let addr = constants::pod_listen_address()
.unwrap_or_else(|| format!("127.0.0.1:{}", constants::DEFAULT_PORT));
let addr = SocketAddr::from_str(&addr).unwrap_or_else(|err| {
error!("Failed to parse desired hosting address {}, {}", addr, err);
std::process::exit(1);
......@@ -286,7 +286,7 @@ pub async fn run_server() {
run Pod with encryption. To set up certificates once you obtained them, \
set {} environment variable to the path \
of the certificates (without .crt and .key suffixes)",
configuration::HTTPS_CERTIFICATE_ENV_NAME
constants::HTTPS_CERTIFICATE_ENV_NAME
);
}
if !is_loopback && check_public_ip(addr.ip()) {
......@@ -299,11 +299,11 @@ pub async fn run_server() {
addr
);
};
if !is_loopback && !configuration::use_insecure_non_tls() {
if !is_loopback && !constants::use_insecure_non_tls() {
error!(
"Refusing to run pod without TLS (https). If you want to override this, \
start pod with environment variable {} set to any value.",
configuration::USE_INSECURE_NON_TLS_ENV_NAME
constants::USE_INSECURE_NON_TLS_ENV_NAME
);
std::process::exit(1)
}
......
......@@ -6,7 +6,7 @@ use crate::api_model::RunDownloader;
use crate::api_model::RunImporter;
use crate::api_model::RunIndexer;
use crate::api_model::UpdateItem;
use crate::configuration;
use crate::constants;
use crate::database_migrate_refinery;
use crate::database_migrate_schema;
use crate::error::Error;
......@@ -204,7 +204,7 @@ fn initialize_db(
database_key: &str,
) -> Result<Connection> {
let database_path = format!("{}.db", &owner);
let database_path = PathBuf::from(configuration::DATABASE_DIR).join(database_path);
let database_path = PathBuf::from(constants::DATABASE_DIR).join(database_path);
let mut conn = Connection::open(database_path).unwrap();
let pragma_sql = format!("PRAGMA key = \"x'{}'\";", database_key);
conn.execute_batch(&pragma_sql)?;
......@@ -221,7 +221,7 @@ fn initialize_db(
}
fn allowed_owner_hashes_fn() -> HashSet<Vec<u8>> {
if let Some(owners) = configuration::pod_owners() {
if let Some(owners) = constants::pod_owners() {
let mut result = HashSet::new();
for owner in owners.split(',') {
let hexed = hex::decode(owner).unwrap_or_else(|err| {
......@@ -252,7 +252,7 @@ fn hash_of_hex(hex_string: &str) -> Result<GenericArray<u8, U32>> {
}
fn check_owner(possible_owner: &str) -> Result<()> {
if configuration::pod_owners().iter().any(|e| e == "ANY") {
if constants::pod_owners().iter().any(|e| e == "ANY") {
return Ok(());
};
let possible_hash = hash_of_hex(possible_owner)?;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment