2020-04-19 17:59:59 +00:00
#![ recursion_limit = " 1024 " ]
2021-04-06 03:25:28 +00:00
//! Garage CLI, used to interact with a running Garage instance, and to launch a Garage instance
2020-04-19 17:59:59 +00:00
2020-04-21 12:54:55 +00:00
#[ macro_use ]
2022-02-17 22:28:23 +00:00
extern crate tracing ;
2020-04-21 12:54:55 +00:00
2021-10-19 14:16:10 +00:00
mod admin ;
2021-03-12 17:16:03 +00:00
mod cli ;
2020-04-24 10:10:01 +00:00
mod repair ;
2024-01-15 16:18:46 +00:00
mod secrets ;
2020-04-10 20:01:48 +00:00
mod server ;
2022-09-03 21:40:44 +00:00
#[ cfg(feature = " telemetry-otlp " ) ]
2022-05-24 10:16:39 +00:00
mod tracing_setup ;
2020-04-05 21:33:42 +00:00
2022-09-07 15:57:12 +00:00
#[ cfg(not(any(feature = " bundled-libs " , feature = " system-libs " ))) ]
compile_error! ( " Either bundled-libs or system-libs Cargo feature must be enabled " ) ;
#[ cfg(all(feature = " bundled-libs " , feature = " system-libs " )) ]
compile_error! ( " Only one of bundled-libs and system-libs Cargo features must be enabled " ) ;
2022-11-08 13:23:08 +00:00
#[ cfg(not(any(feature = " lmdb " , feature = " sled " , feature = " sqlite " ))) ]
compile_error! ( " Must activate the Cargo feature for at least one DB engine: lmdb, sled or sqlite. " ) ;
2021-10-26 09:22:28 +00:00
use std ::net ::SocketAddr ;
2021-10-19 14:16:10 +00:00
use std ::path ::PathBuf ;
2020-04-05 21:33:42 +00:00
use structopt ::StructOpt ;
2024-02-13 11:55:41 +00:00
use garage_net ::util ::parse_and_resolve_peer_addr ;
use garage_net ::NetworkKey ;
2021-10-14 09:50:12 +00:00
2021-10-19 14:16:10 +00:00
use garage_util ::error ::* ;
2020-04-23 17:05:46 +00:00
2021-10-14 09:50:12 +00:00
use garage_rpc ::system ::* ;
use garage_rpc ::* ;
2020-04-19 11:22:28 +00:00
2022-01-03 12:58:05 +00:00
use garage_model ::helper ::error ::Error as HelperError ;
2021-10-19 14:16:10 +00:00
use admin ::* ;
2021-03-12 17:16:03 +00:00
use cli ::* ;
2024-01-15 16:18:46 +00:00
use secrets ::Secrets ;
2020-04-19 15:15:48 +00:00
2020-04-05 21:33:42 +00:00
#[ derive(StructOpt, Debug) ]
2022-09-07 15:05:21 +00:00
#[ structopt(
name = " garage " ,
about = " S3-compatible object store for self-hosted geo-distributed deployments "
) ]
2021-03-26 21:36:23 +00:00
struct Opt {
2024-01-16 13:04:11 +00:00
/// Host to connect to for admin operations, in the format: <full-node-id>@<ip>:<port>
2021-10-28 15:13:13 +00:00
#[ structopt(short = " h " , long = " rpc-host " , env = " GARAGE_RPC_HOST " ) ]
2021-10-14 09:50:12 +00:00
pub rpc_host : Option < String > ,
2020-04-06 17:55:39 +00:00
2023-02-03 14:27:39 +00:00
#[ structopt(flatten) ]
pub secrets : Secrets ,
2020-04-12 17:41:19 +00:00
2023-02-03 14:27:39 +00:00
/// Path to configuration file
2021-10-28 15:13:13 +00:00
#[ structopt(
short = " c " ,
long = " config " ,
env = " GARAGE_CONFIG_FILE " ,
default_value = " /etc/garage.toml "
) ]
2021-10-19 14:16:10 +00:00
pub config_file : PathBuf ,
2020-04-07 14:26:22 +00:00
#[ structopt(subcommand) ]
cmd : Command ,
2020-04-05 21:33:42 +00:00
}
#[ tokio::main ]
async fn main ( ) {
2022-09-07 16:30:15 +00:00
// Initialize version and features info
2022-09-07 15:05:21 +00:00
let features = & [
#[ cfg(feature = " k2v " ) ]
" k2v " ,
#[ cfg(feature = " sled " ) ]
" sled " ,
#[ cfg(feature = " lmdb " ) ]
" lmdb " ,
#[ cfg(feature = " sqlite " ) ]
" sqlite " ,
2022-10-18 16:38:20 +00:00
#[ cfg(feature = " consul-discovery " ) ]
" consul-discovery " ,
2022-09-07 15:05:21 +00:00
#[ cfg(feature = " kubernetes-discovery " ) ]
" kubernetes-discovery " ,
#[ cfg(feature = " metrics " ) ]
" metrics " ,
#[ cfg(feature = " telemetry-otlp " ) ]
" telemetry-otlp " ,
#[ cfg(feature = " bundled-libs " ) ]
" bundled-libs " ,
#[ cfg(feature = " system-libs " ) ]
" system-libs " ,
] [ .. ] ;
2022-09-07 16:30:15 +00:00
if let Some ( git_version ) = option_env! ( " GIT_VERSION " ) {
2022-09-07 16:36:46 +00:00
garage_util ::version ::init_version ( git_version ) ;
2023-05-03 14:43:08 +00:00
} else {
garage_util ::version ::init_version ( git_version ::git_version! (
prefix = " git: " ,
cargo_prefix = " cargo: " ,
fallback = " unknown "
) ) ;
2022-09-07 16:30:15 +00:00
}
2022-09-07 16:36:46 +00:00
garage_util ::version ::init_features ( features ) ;
2022-09-07 16:30:15 +00:00
2022-09-07 15:05:21 +00:00
let version = format! (
" {} [features: {}] " ,
2022-09-07 16:36:46 +00:00
garage_util ::version ::garage_version ( ) ,
2022-09-07 15:05:21 +00:00
features . join ( " , " )
) ;
2022-11-04 15:33:05 +00:00
// Initialize panic handler that aborts on panic and shows a nice message.
// By default, Tokio continues runing normally when a task panics. We want
// to avoid this behavior in Garage as this would risk putting the process in an
// unknown/uncontrollable state. We prefer to exit the process and restart it
// from scratch, so that it boots back into a fresh, known state.
let panic_version_info = version . clone ( ) ;
std ::panic ::set_hook ( Box ::new ( move | panic_info | {
eprintln! ( " ======== PANIC (internal Garage error) ======== " ) ;
eprintln! ( " {} " , panic_info ) ;
eprintln! ( ) ;
eprintln! ( " Panics are internal errors that Garage is unable to handle on its own. " ) ;
eprintln! ( " They can be caused by bugs in Garage's code, or by corrupted data in " ) ;
eprintln! ( " the node's storage. If you feel that this error is likely to be a bug " ) ;
eprintln! ( " in Garage, please report it on our issue tracker a the following address: " ) ;
eprintln! ( ) ;
eprintln! ( " https://git.deuxfleurs.fr/Deuxfleurs/garage/issues " ) ;
eprintln! ( ) ;
eprintln! ( " Please include the last log messages and the the full backtrace below in " ) ;
eprintln! ( " your bug report, as well as any relevant information on the context in " ) ;
eprintln! ( " which Garage was running when this error occurred. " ) ;
eprintln! ( ) ;
eprintln! ( " GARAGE VERSION: {} " , panic_version_info ) ;
eprintln! ( ) ;
eprintln! ( " BACKTRACE: " ) ;
eprintln! ( " {:?} " , backtrace ::Backtrace ::new ( ) ) ;
std ::process ::abort ( ) ;
} ) ) ;
2022-12-12 16:16:49 +00:00
// Parse arguments and dispatch command line
let opt = Opt ::from_clap ( & Opt ::clap ( ) . version ( version . as_str ( ) ) . get_matches ( ) ) ;
2022-11-04 15:33:05 +00:00
// Initialize logging as well as other libraries used in Garage
if std ::env ::var ( " RUST_LOG " ) . is_err ( ) {
2022-12-12 16:16:49 +00:00
let default_log = match & opt . cmd {
Command ::Server = > " netapp=info,garage=info " ,
_ = > " netapp=warn,garage=warn " ,
} ;
std ::env ::set_var ( " RUST_LOG " , default_log )
2022-11-04 15:33:05 +00:00
}
tracing_subscriber ::fmt ( )
. with_writer ( std ::io ::stderr )
. with_env_filter ( tracing_subscriber ::filter ::EnvFilter ::from_default_env ( ) )
. init ( ) ;
sodiumoxide ::init ( ) . expect ( " Unable to init sodiumoxide " ) ;
2021-10-19 14:16:10 +00:00
let res = match opt . cmd {
2023-02-03 14:27:39 +00:00
Command ::Server = > server ::run_server ( opt . config_file , opt . secrets ) . await ,
2022-06-15 18:20:28 +00:00
Command ::OfflineRepair ( repair_opt ) = > {
2023-02-03 14:27:39 +00:00
repair ::offline ::offline_repair ( opt . config_file , opt . secrets , repair_opt ) . await
2021-10-19 14:16:10 +00:00
}
2023-10-10 10:06:27 +00:00
Command ::ConvertDb ( conv_opt ) = > {
cli ::convert_db ::do_conversion ( conv_opt ) . map_err ( From ::from )
}
2021-11-09 11:24:04 +00:00
Command ::Node ( NodeOperation ::NodeId ( node_id_opt ) ) = > {
node_id_command ( opt . config_file , node_id_opt . quiet )
}
2021-10-19 14:16:10 +00:00
_ = > cli_command ( opt ) . await ,
2021-03-12 17:12:31 +00:00
} ;
if let Err ( e ) = res {
2021-10-19 14:16:10 +00:00
eprintln! ( " Error: {} " , e ) ;
std ::process ::exit ( 1 ) ;
2021-03-12 17:12:31 +00:00
}
}
async fn cli_command ( opt : Opt ) -> Result < ( ) , Error > {
2024-02-12 09:42:17 +00:00
let config = if ( opt . secrets . rpc_secret . is_none ( ) & & opt . secrets . rpc_secret_file . is_none ( ) )
| | opt . rpc_host . is_none ( )
{
2021-10-19 14:16:10 +00:00
Some ( garage_util ::config ::read_config ( opt . config_file . clone ( ) )
. err_context ( format! ( " Unable to read configuration file {} . Configuration file is needed because -h or -s is not provided on the command line. " , opt . config_file . to_string_lossy ( ) ) ) ? )
} else {
None
} ;
// Find and parse network RPC secret
2024-02-12 09:42:17 +00:00
let mut rpc_secret = config . as_ref ( ) . and_then ( | c | c . rpc_secret . clone ( ) ) ;
secrets ::fill_secret (
& mut rpc_secret ,
& config . as_ref ( ) . and_then ( | c | c . rpc_secret_file . clone ( ) ) ,
& opt . secrets . rpc_secret ,
& opt . secrets . rpc_secret_file ,
" rpc_secret " ,
true ,
) ? ;
let net_key_hex_str = rpc_secret . ok_or ( " No RPC secret provided " ) ? ;
2021-10-14 09:50:12 +00:00
let network_key = NetworkKey ::from_slice (
2024-02-12 09:42:17 +00:00
& hex ::decode ( & net_key_hex_str ) . err_context ( " Invalid RPC secret key (bad hex) " ) ? [ .. ] ,
2021-10-14 09:50:12 +00:00
)
2021-10-19 14:16:10 +00:00
. ok_or ( " Invalid RPC secret provided (wrong length) " ) ? ;
// Generate a temporary keypair for our RPC client
2021-10-14 09:50:12 +00:00
let ( _pk , sk ) = sodiumoxide ::crypto ::sign ::ed25519 ::gen_keypair ( ) ;
2024-02-19 10:24:33 +00:00
let netapp = NetApp ::new ( GARAGE_VERSION_TAG , network_key , sk , None ) ;
2021-10-19 14:16:10 +00:00
// Find and parse the address of the target host
2022-12-12 16:16:49 +00:00
let ( id , addr , is_default_addr ) = if let Some ( h ) = opt . rpc_host {
2024-01-16 13:04:11 +00:00
let ( id , addrs ) = parse_and_resolve_peer_addr ( & h ) . ok_or_else ( | | format! ( " Invalid RPC remote node identifier: {} . Expected format is <full node id>@<IP or hostname>:<port>. " , h ) ) ? ;
2022-12-12 16:16:49 +00:00
( id , addrs [ 0 ] , false )
2021-10-19 14:16:10 +00:00
} else {
2021-10-26 09:22:28 +00:00
let node_id = garage_rpc ::system ::read_node_id ( & config . as_ref ( ) . unwrap ( ) . metadata_dir )
. err_context ( READ_KEY_ERROR ) ? ;
2022-09-14 14:09:38 +00:00
if let Some ( a ) = config . as_ref ( ) . and_then ( | c | c . rpc_public_addr . as_ref ( ) ) {
use std ::net ::ToSocketAddrs ;
let a = a
. to_socket_addrs ( )
. ok_or_message ( " unable to resolve rpc_public_addr specified in config file " ) ?
. next ( )
. ok_or_message ( " unable to resolve rpc_public_addr specified in config file " ) ? ;
2022-12-12 16:16:49 +00:00
( node_id , a , false )
2021-10-26 09:22:28 +00:00
} else {
let default_addr = SocketAddr ::new (
" 127.0.0.1 " . parse ( ) . unwrap ( ) ,
config . as_ref ( ) . unwrap ( ) . rpc_bind_addr . port ( ) ,
) ;
2022-12-12 16:16:49 +00:00
( node_id , default_addr , true )
2021-10-26 09:22:28 +00:00
}
2021-10-19 14:16:10 +00:00
} ;
// Connect to target host
2022-12-12 16:16:49 +00:00
if let Err ( e ) = netapp . clone ( ) . try_connect ( addr , id ) . await {
if is_default_addr {
warn! (
" Tried to contact Garage node at default address {}, which didn't work. If that address is wrong, consider setting rpc_public_addr in your config file. " ,
addr
) ;
}
2024-01-16 13:04:11 +00:00
Err ( e ) . err_context ( " Unable to connect to destination RPC host. Check that you are using the same value of rpc_secret as them, and that you have their correct full-length node ID (public key). " ) ? ;
2022-12-12 16:16:49 +00:00
}
2021-10-14 09:50:12 +00:00
let system_rpc_endpoint = netapp . endpoint ::< SystemRpc , ( ) > ( SYSTEM_RPC_PATH . into ( ) ) ;
let admin_rpc_endpoint = netapp . endpoint ::< AdminRpc , ( ) > ( ADMIN_RPC_PATH . into ( ) ) ;
2022-01-03 12:58:05 +00:00
match cli_command_dispatch ( opt . cmd , & system_rpc_endpoint , & admin_rpc_endpoint , id ) . await {
2022-01-03 18:06:04 +00:00
Err ( HelperError ::Internal ( i ) ) = > Err ( Error ::Message ( format! ( " Internal error: {} " , i ) ) ) ,
Err ( HelperError ::BadRequest ( b ) ) = > Err ( Error ::Message ( b ) ) ,
2022-05-24 10:16:39 +00:00
Err ( e ) = > Err ( Error ::Message ( format! ( " {} " , e ) ) ) ,
2022-01-03 12:58:05 +00:00
Ok ( x ) = > Ok ( x ) ,
}
2021-06-01 17:05:15 +00:00
}