2021-02-16 13:48:20 -08:00
|
|
|
#![allow(clippy::integer_arithmetic)]
|
2019-03-15 10:54:54 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2021-03-11 17:52:16 -08:00
|
|
|
use {
|
|
|
|
clap::{crate_description, crate_name, App, AppSettings, Arg, ArgMatches, SubCommand},
|
|
|
|
solana_clap_utils::{
|
|
|
|
input_parsers::pubkey_of,
|
|
|
|
input_validators::{is_pubkey, is_url},
|
|
|
|
},
|
2020-03-17 20:03:58 -07:00
|
|
|
};
|
2019-03-13 15:31:05 -07:00
|
|
|
|
2019-03-15 10:54:54 -07:00
|
|
|
mod build_env;
|
|
|
|
mod command;
|
|
|
|
mod config;
|
|
|
|
mod defaults;
|
2019-07-01 14:08:30 -07:00
|
|
|
mod stop_process;
|
2019-03-15 10:54:54 -07:00
|
|
|
mod update_manifest;
|
2019-03-13 15:31:05 -07:00
|
|
|
|
2020-02-14 19:35:40 -08:00
|
|
|
pub fn is_semver(semver: &str) -> Result<(), String> {
|
2021-06-18 06:34:46 -07:00
|
|
|
match semver::Version::parse(semver) {
|
2020-02-14 19:35:40 -08:00
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(err) => Err(format!("{:?}", err)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_release_channel(channel: &str) -> Result<(), String> {
|
|
|
|
match channel {
|
|
|
|
"edge" | "beta" | "stable" => Ok(()),
|
|
|
|
_ => Err(format!("Invalid release channel {}", channel)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_explicit_release(string: String) -> Result<(), String> {
|
|
|
|
if string.starts_with('v') && is_semver(string.split_at(1).1).is_ok() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
is_semver(&string).or_else(|_| is_release_channel(&string))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn explicit_release_of(
|
|
|
|
matches: &ArgMatches<'_>,
|
|
|
|
name: &str,
|
|
|
|
) -> Option<config::ExplicitRelease> {
|
|
|
|
matches
|
|
|
|
.value_of(name)
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.map(|explicit_release| {
|
|
|
|
if explicit_release.starts_with('v')
|
|
|
|
&& is_semver(explicit_release.split_at(1).1).is_ok()
|
|
|
|
{
|
|
|
|
config::ExplicitRelease::Semver(explicit_release.split_at(1).1.to_string())
|
|
|
|
} else if is_semver(&explicit_release).is_ok() {
|
|
|
|
config::ExplicitRelease::Semver(explicit_release)
|
|
|
|
} else {
|
|
|
|
config::ExplicitRelease::Channel(explicit_release)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-17 20:03:58 -07:00
|
|
|
fn handle_init(matches: &ArgMatches<'_>, config_file: &str) -> Result<(), String> {
|
|
|
|
let json_rpc_url = matches.value_of("json_rpc_url").unwrap();
|
2021-06-18 06:34:46 -07:00
|
|
|
let update_manifest_pubkey = pubkey_of(matches, "update_manifest_pubkey");
|
2020-03-17 20:03:58 -07:00
|
|
|
let data_dir = matches.value_of("data_dir").unwrap();
|
|
|
|
let no_modify_path = matches.is_present("no_modify_path");
|
2021-06-18 06:34:46 -07:00
|
|
|
let explicit_release = explicit_release_of(matches, "explicit_release");
|
2020-03-17 20:03:58 -07:00
|
|
|
|
|
|
|
if update_manifest_pubkey.is_none() && explicit_release.is_none() {
|
|
|
|
Err(format!(
|
|
|
|
"Please specify the release to install for {}. See --help for more",
|
|
|
|
build_env::TARGET
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
command::init(
|
|
|
|
config_file,
|
|
|
|
data_dir,
|
|
|
|
json_rpc_url,
|
|
|
|
&update_manifest_pubkey.unwrap_or_default(),
|
|
|
|
no_modify_path,
|
|
|
|
explicit_release,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-08 19:01:22 -07:00
|
|
|
pub fn main() -> Result<(), String> {
|
2019-03-13 15:31:05 -07:00
|
|
|
solana_logger::setup();
|
|
|
|
|
|
|
|
let matches = App::new(crate_name!())
|
|
|
|
.about(crate_description!())
|
2020-05-11 15:02:01 -07:00
|
|
|
.version(solana_version::version!())
|
2019-03-15 10:54:54 -07:00
|
|
|
.setting(AppSettings::SubcommandRequiredElseHelp)
|
|
|
|
.arg({
|
|
|
|
let arg = Arg::with_name("config_file")
|
|
|
|
.short("c")
|
|
|
|
.long("config")
|
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
2019-08-09 15:57:31 -07:00
|
|
|
.global(true)
|
2019-03-15 10:54:54 -07:00
|
|
|
.help("Configuration file to use");
|
|
|
|
match *defaults::CONFIG_FILE {
|
2021-06-18 06:34:46 -07:00
|
|
|
Some(ref config_file) => arg.default_value(config_file),
|
2019-03-15 10:54:54 -07:00
|
|
|
None => arg.required(true),
|
|
|
|
}
|
|
|
|
})
|
2019-03-13 15:31:05 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("init")
|
|
|
|
.about("initializes a new installation")
|
|
|
|
.setting(AppSettings::DisableVersion)
|
2019-03-15 10:54:54 -07:00
|
|
|
.arg({
|
|
|
|
let arg = Arg::with_name("data_dir")
|
|
|
|
.short("d")
|
2019-05-27 07:31:50 -07:00
|
|
|
.long("data-dir")
|
2019-03-15 10:54:54 -07:00
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
2019-03-20 16:12:50 -07:00
|
|
|
.required(true)
|
2019-03-15 10:54:54 -07:00
|
|
|
.help("Directory to store install data");
|
|
|
|
match *defaults::DATA_DIR {
|
2021-06-18 06:34:46 -07:00
|
|
|
Some(ref data_dir) => arg.default_value(data_dir),
|
2019-03-20 16:12:50 -07:00
|
|
|
None => arg,
|
2019-03-15 10:54:54 -07:00
|
|
|
}
|
|
|
|
})
|
2019-03-13 15:31:05 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("json_rpc_url")
|
|
|
|
.short("u")
|
|
|
|
.long("url")
|
|
|
|
.value_name("URL")
|
|
|
|
.takes_value(true)
|
2019-03-15 10:54:54 -07:00
|
|
|
.default_value(defaults::JSON_RPC_URL)
|
2019-05-06 07:38:26 -07:00
|
|
|
.validator(is_url)
|
2019-03-13 15:31:05 -07:00
|
|
|
.help("JSON RPC URL for the solana cluster"),
|
|
|
|
)
|
2019-03-21 12:00:45 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("no_modify_path")
|
|
|
|
.long("no-modify-path")
|
|
|
|
.help("Don't configure the PATH environment variable"),
|
|
|
|
)
|
2020-03-17 20:07:37 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("update_manifest_pubkey")
|
2019-03-13 15:31:05 -07:00
|
|
|
.short("p")
|
|
|
|
.long("pubkey")
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
2019-05-06 07:38:26 -07:00
|
|
|
.validator(is_pubkey)
|
2020-03-17 20:07:37 -07:00
|
|
|
.help("Public key of the update manifest"),
|
|
|
|
)
|
2019-07-23 12:51:10 -07:00
|
|
|
.arg(
|
2019-07-31 17:30:17 -07:00
|
|
|
Arg::with_name("explicit_release")
|
|
|
|
.value_name("release")
|
2019-07-23 12:51:10 -07:00
|
|
|
.index(1)
|
|
|
|
.conflicts_with_all(&["json_rpc_url", "update_manifest_pubkey"])
|
2020-02-14 19:35:40 -08:00
|
|
|
.validator(is_explicit_release)
|
2020-03-17 20:03:58 -07:00
|
|
|
.help("The release version or channel to install"),
|
2019-07-23 12:51:10 -07:00
|
|
|
),
|
2019-03-13 15:31:05 -07:00
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("info")
|
2021-01-29 12:44:44 -08:00
|
|
|
.about("Displays information about the current installation")
|
2019-03-13 15:31:05 -07:00
|
|
|
.setting(AppSettings::DisableVersion)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("local_info_only")
|
|
|
|
.short("l")
|
|
|
|
.long("local")
|
2021-03-11 17:52:16 -08:00
|
|
|
.help("only display local information, don't check for updates"),
|
2020-10-20 12:09:12 -07:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("eval")
|
|
|
|
.long("eval")
|
|
|
|
.help("display information in a format that can be used with `eval`"),
|
2019-03-13 15:31:05 -07:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("deploy")
|
2021-01-29 12:44:44 -08:00
|
|
|
.about("Deploys a new update")
|
2019-03-13 15:31:05 -07:00
|
|
|
.setting(AppSettings::DisableVersion)
|
2019-03-20 16:12:50 -07:00
|
|
|
.arg({
|
|
|
|
let arg = Arg::with_name("from_keypair_file")
|
|
|
|
.short("k")
|
|
|
|
.long("keypair")
|
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("Keypair file of the account that funds the deployment");
|
|
|
|
match *defaults::USER_KEYPAIR {
|
2021-06-18 06:34:46 -07:00
|
|
|
Some(ref config_file) => arg.default_value(config_file),
|
2019-03-20 16:12:50 -07:00
|
|
|
None => arg,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("json_rpc_url")
|
|
|
|
.short("u")
|
|
|
|
.long("url")
|
|
|
|
.value_name("URL")
|
|
|
|
.takes_value(true)
|
|
|
|
.default_value(defaults::JSON_RPC_URL)
|
2019-05-06 07:38:26 -07:00
|
|
|
.validator(is_url)
|
2019-03-20 16:12:50 -07:00
|
|
|
.help("JSON RPC URL for the solana cluster"),
|
|
|
|
)
|
2019-03-13 15:31:05 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("download_url")
|
|
|
|
.index(1)
|
|
|
|
.required(true)
|
2019-05-06 07:38:26 -07:00
|
|
|
.validator(is_url)
|
2019-03-13 15:31:05 -07:00
|
|
|
.help("URL to the solana release archive"),
|
|
|
|
)
|
|
|
|
.arg(
|
2019-03-20 16:12:50 -07:00
|
|
|
Arg::with_name("update_manifest_keypair_file")
|
2019-03-13 15:31:05 -07:00
|
|
|
.index(2)
|
|
|
|
.required(true)
|
|
|
|
.help("Keypair file for the update manifest (/path/to/keypair.json)"),
|
|
|
|
),
|
|
|
|
)
|
2021-01-29 15:38:54 -08:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("gc")
|
|
|
|
.about("Delete older releases from the install cache to reclaim disk space")
|
|
|
|
.setting(AppSettings::DisableVersion),
|
|
|
|
)
|
2019-03-13 15:31:05 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("update")
|
2021-01-29 12:44:44 -08:00
|
|
|
.about("Checks for an update, and if available downloads and applies it")
|
2019-03-13 15:31:05 -07:00
|
|
|
.setting(AppSettings::DisableVersion),
|
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("run")
|
|
|
|
.about("Runs a program while periodically checking and applying software updates")
|
|
|
|
.after_help("The program will be restarted upon a successful software update")
|
|
|
|
.setting(AppSettings::DisableVersion)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("program_name")
|
|
|
|
.index(1)
|
|
|
|
.required(true)
|
|
|
|
.help("program to run"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("program_arguments")
|
|
|
|
.index(2)
|
|
|
|
.multiple(true)
|
|
|
|
.help("arguments to supply to the program"),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.get_matches();
|
|
|
|
|
2019-03-15 10:54:54 -07:00
|
|
|
let config_file = matches.value_of("config_file").unwrap();
|
|
|
|
|
|
|
|
match matches.subcommand() {
|
2021-06-18 06:34:46 -07:00
|
|
|
("init", Some(matches)) => handle_init(matches, config_file),
|
2019-03-15 10:54:54 -07:00
|
|
|
("info", Some(matches)) => {
|
|
|
|
let local_info_only = matches.is_present("local_info_only");
|
2020-10-20 12:09:12 -07:00
|
|
|
let eval = matches.is_present("eval");
|
|
|
|
command::info(config_file, local_info_only, eval).map(|_| ())
|
2019-03-15 10:54:54 -07:00
|
|
|
}
|
|
|
|
("deploy", Some(matches)) => {
|
2019-03-20 16:12:50 -07:00
|
|
|
let from_keypair_file = matches.value_of("from_keypair_file").unwrap();
|
|
|
|
let json_rpc_url = matches.value_of("json_rpc_url").unwrap();
|
2019-03-15 10:54:54 -07:00
|
|
|
let download_url = matches.value_of("download_url").unwrap();
|
2019-03-20 16:12:50 -07:00
|
|
|
let update_manifest_keypair_file =
|
|
|
|
matches.value_of("update_manifest_keypair_file").unwrap();
|
|
|
|
command::deploy(
|
|
|
|
json_rpc_url,
|
|
|
|
from_keypair_file,
|
|
|
|
download_url,
|
|
|
|
update_manifest_keypair_file,
|
|
|
|
)
|
2019-03-15 10:54:54 -07:00
|
|
|
}
|
2021-01-29 15:38:54 -08:00
|
|
|
("gc", Some(_matches)) => command::gc(config_file),
|
2021-03-11 17:52:16 -08:00
|
|
|
("update", Some(_matches)) => command::update(config_file, false).map(|_| ()),
|
2019-03-15 10:54:54 -07:00
|
|
|
("run", Some(matches)) => {
|
|
|
|
let program_name = matches.value_of("program_name").unwrap();
|
|
|
|
let program_arguments = matches
|
|
|
|
.values_of("program_arguments")
|
2019-04-15 14:56:08 -07:00
|
|
|
.map(Iterator::collect)
|
2020-06-08 17:38:14 -07:00
|
|
|
.unwrap_or_else(Vec::new);
|
2019-03-15 10:54:54 -07:00
|
|
|
|
|
|
|
command::run(config_file, program_name, program_arguments)
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2019-03-13 15:31:05 -07:00
|
|
|
}
|
2019-06-08 19:01:22 -07:00
|
|
|
|
|
|
|
pub fn main_init() -> Result<(), String> {
|
|
|
|
solana_logger::setup();
|
|
|
|
|
|
|
|
let matches = App::new("solana-install-init")
|
2021-01-29 12:44:44 -08:00
|
|
|
.about("Initializes a new installation")
|
2020-05-11 15:02:01 -07:00
|
|
|
.version(solana_version::version!())
|
2019-06-08 19:01:22 -07:00
|
|
|
.arg({
|
|
|
|
let arg = Arg::with_name("config_file")
|
|
|
|
.short("c")
|
|
|
|
.long("config")
|
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Configuration file to use");
|
|
|
|
match *defaults::CONFIG_FILE {
|
2021-06-18 06:34:46 -07:00
|
|
|
Some(ref config_file) => arg.default_value(config_file),
|
2019-06-08 19:01:22 -07:00
|
|
|
None => arg.required(true),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.arg({
|
|
|
|
let arg = Arg::with_name("data_dir")
|
|
|
|
.short("d")
|
|
|
|
.long("data-dir")
|
|
|
|
.value_name("PATH")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("Directory to store install data");
|
|
|
|
match *defaults::DATA_DIR {
|
2021-06-18 06:34:46 -07:00
|
|
|
Some(ref data_dir) => arg.default_value(data_dir),
|
2019-06-08 19:01:22 -07:00
|
|
|
None => arg,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("json_rpc_url")
|
|
|
|
.short("u")
|
|
|
|
.long("url")
|
|
|
|
.value_name("URL")
|
|
|
|
.takes_value(true)
|
|
|
|
.default_value(defaults::JSON_RPC_URL)
|
|
|
|
.validator(is_url)
|
|
|
|
.help("JSON RPC URL for the solana cluster"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("no_modify_path")
|
|
|
|
.long("no-modify-path")
|
|
|
|
.help("Don't configure the PATH environment variable"),
|
|
|
|
)
|
2020-03-17 20:07:37 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("update_manifest_pubkey")
|
2019-06-08 19:01:22 -07:00
|
|
|
.short("p")
|
|
|
|
.long("pubkey")
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.validator(is_pubkey)
|
2020-03-17 20:07:37 -07:00
|
|
|
.help("Public key of the update manifest"),
|
|
|
|
)
|
2019-07-23 12:51:10 -07:00
|
|
|
.arg(
|
2019-07-31 17:30:17 -07:00
|
|
|
Arg::with_name("explicit_release")
|
|
|
|
.value_name("release")
|
2019-07-23 12:51:10 -07:00
|
|
|
.index(1)
|
|
|
|
.conflicts_with_all(&["json_rpc_url", "update_manifest_pubkey"])
|
2020-02-14 19:35:40 -08:00
|
|
|
.validator(is_explicit_release)
|
2020-03-17 20:03:58 -07:00
|
|
|
.help("The release version or channel to install"),
|
2019-07-23 12:51:10 -07:00
|
|
|
)
|
2019-06-08 19:01:22 -07:00
|
|
|
.get_matches();
|
|
|
|
|
|
|
|
let config_file = matches.value_of("config_file").unwrap();
|
2021-06-18 06:34:46 -07:00
|
|
|
handle_init(&matches, config_file)
|
2019-06-08 19:01:22 -07:00
|
|
|
}
|