Add command plumbing

This commit is contained in:
Michael Vines
2019-03-15 10:54:54 -07:00
parent c8bbca08f8
commit 1d876df8b3
8 changed files with 277 additions and 22 deletions

View File

@ -13,8 +13,12 @@ bincode = "1.1.2"
bs58 = "0.2.0"
clap = { version = "2.32.0"}
chrono = { version = "0.4.0", features = ["serde"] }
dirs = "1.0.5"
lazy_static = "1.3.0"
log = "0.4.2"
serde_json = "1.0.39"
serde = "1.0.89"
serde_derive = "1.0.89"
serde_yaml = "0.8.8"
solana-client = { path = "../client", version = "0.13.0" }
solana-logger = { path = "../logger", version = "0.13.0" }
solana-sdk = { path = "../sdk", version = "0.13.0" }

2
install/src/build_env.rs Normal file
View File

@ -0,0 +1,2 @@
pub const TARGET: &str = env!("TARGET");
pub const BUILD_SECONDS_SINCE_UNIX_EPOCH: &str = env!("BUILD_SECONDS_SINCE_UNIX_EPOCH");

65
install/src/command.rs Normal file
View File

@ -0,0 +1,65 @@
use crate::config::Config;
use solana_sdk::pubkey::Pubkey;
use std::time::Duration;
pub fn init(
config_file: &str,
data_dir: &str,
json_rpc_url: &str,
update_pubkey: &Pubkey,
) -> Result<(), String> {
println!("init {:?} {:?}", json_rpc_url, update_pubkey);
Config {
data_dir: data_dir.to_string(),
json_rpc_url: json_rpc_url.to_string(),
update_pubkey: *update_pubkey,
current_install: None,
}
.save(config_file)?;
Err("Not implemented".to_string())
}
pub fn info(config_file: &str, local_info_only: bool) -> Result<(), String> {
let config = Config::load(config_file)?;
println!("config dir: {:?}", config_file);
println!("configuration: {:?}", config);
if local_info_only {
return Ok(());
}
// TODO: fetch info about current update manifest from the cluster
Err("Not implemented".to_string())
}
pub fn deploy(
config_file: &str,
download_url: &str,
update_manifest_keypair: &str,
) -> Result<(), String> {
println!("deploy {:?} {:?}", download_url, update_manifest_keypair);
let _config = Config::load(config_file)?;
Err("Not implemented".to_string())
}
pub fn update(config_file: &str) -> Result<(), String> {
println!(
"update: BUILD_SECONDS_SINCE_UNIX_EPOCH={:?}",
Duration::from_secs(
u64::from_str_radix(crate::build_env::BUILD_SECONDS_SINCE_UNIX_EPOCH, 10).unwrap()
)
);
let _config = Config::load(config_file)?;
Err("Not implemented".to_string())
}
pub fn run(
config_file: &str,
program_name: &str,
program_arguments: Vec<&str>,
) -> Result<(), String> {
println!("run {:?} {:?}", program_name, program_arguments);
let _config = Config::load(config_file)?;
Err("Not implemented".to_string())
}

46
install/src/config.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::update_manifest::SignedUpdateManifest;
use serde_derive::{Deserialize, Serialize};
use serde_yaml;
use solana_sdk::pubkey::Pubkey;
use std::fs::{create_dir_all, File};
use std::io::{Error, ErrorKind, Write};
use std::path::Path;
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct Config {
pub data_dir: String,
pub json_rpc_url: String,
pub update_pubkey: Pubkey,
pub current_install: Option<SignedUpdateManifest>,
}
impl Config {
fn _load(config_file: &str) -> Result<Self, Error> {
let file = File::open(config_file.to_string())?;
let config = serde_yaml::from_reader(file)
.map_err(|err| Error::new(ErrorKind::Other, format!("{:?}", err)))?;
Ok(config)
}
pub fn load(config_file: &str) -> Result<Self, String> {
Self::_load(config_file).map_err(|err| format!("Unable to load {}: {:?}", config_file, err))
}
fn _save(&self, config_file: &str) -> Result<(), Error> {
let serialized = serde_yaml::to_string(self)
.map_err(|err| Error::new(ErrorKind::Other, format!("{:?}", err)))?;
if let Some(outdir) = Path::new(&config_file).parent() {
create_dir_all(outdir)?;
}
let mut file = File::create(config_file)?;
file.write_all(&serialized.into_bytes())?;
Ok(())
}
pub fn save(&self, config_file: &str) -> Result<(), String> {
self._save(config_file)
.map_err(|err| format!("Unable to save {}: {:?}", config_file, err))
}
}

25
install/src/defaults.rs Normal file
View File

@ -0,0 +1,25 @@
pub const JSON_RPC_URL: &str = "https://api.testnet.solana.com/";
lazy_static! {
pub static ref CONFIG_FILE: Option<String> = {
dirs::config_dir().map(|mut path| {
path.push("solana");
path.push("install.yml");
path.to_str().unwrap().to_string()
})
};
pub static ref DATA_DIR: Option<String> = {
dirs::data_dir().map(|mut path| {
path.push("solana");
path.to_str().unwrap().to_string()
})
};
}
pub fn update_pubkey(target: &str) -> Option<&str> {
match target {
"x86_64-apple-darwin" => Some("9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp"), // TODO: This pubkey is invalid
"x86_64-unknown-linux-gnu" => Some("8XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp"), // TODO: This pubkey is invalid
_ => None,
}
}

View File

@ -1,40 +1,76 @@
#[macro_use]
extern crate lazy_static;
use clap::{crate_description, crate_name, crate_version, App, AppSettings, Arg, SubCommand};
//use clap::{crate_description, crate_version, load_yaml, App, AppSettings};
use std::error;
use std::time::Duration;
use solana_sdk::pubkey::Pubkey;
const TARGET: &str = env!("TARGET");
const BUILD_SECONDS_SINCE_UNIX_EPOCH: &str = env!("BUILD_SECONDS_SINCE_UNIX_EPOCH");
mod build_env;
mod command;
mod config;
mod defaults;
mod update_manifest;
fn main() -> Result<(), Box<dyn error::Error>> {
fn main() -> Result<(), String> {
solana_logger::setup();
let matches = App::new(crate_name!())
.about(crate_description!())
.version(crate_version!())
.setting(AppSettings::ArgRequiredElseHelp)
.setting(AppSettings::SubcommandRequiredElseHelp)
.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 {
Some(ref config_file) => arg.default_value(&config_file),
None => arg.required(true),
}
})
.subcommand(
SubCommand::with_name("init")
.about("initializes a new installation")
.setting(AppSettings::DisableVersion)
.arg({
let arg = Arg::with_name("data_dir")
.short("d")
.long("data_dir")
.value_name("PATH")
.takes_value(true)
.help("Directory to store install data");
match *defaults::DATA_DIR {
Some(ref data_dir) => arg.default_value(&data_dir),
None => arg.required(true),
}
})
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL")
.takes_value(true)
.default_value("https://api.testnet.solana.com/")
.default_value(defaults::JSON_RPC_URL)
.help("JSON RPC URL for the solana cluster"),
)
.arg(
Arg::with_name("update_pubkey")
.arg({
let arg = Arg::with_name("update_pubkey")
.short("p")
.long("pubkey")
.value_name("PUBKEY")
.takes_value(true)
.default_value("Solana-managed update manifest")
.help("Public key of the update manifest"),
),
.validator(|value| match value.parse::<Pubkey>() {
Ok(_) => Ok(()),
Err(err) => Err(format!("{:?}", err)),
})
.help("Public key of the update manifest");
match defaults::update_pubkey(build_env::TARGET) {
Some(default_value) => arg.default_value(default_value),
None => arg.required(true),
}
}),
)
.subcommand(
SubCommand::with_name("info")
@ -91,11 +127,38 @@ fn main() -> Result<(), Box<dyn error::Error>> {
)
.get_matches();
println!("TARGET={}", TARGET);
println!(
"BUILD_SECONDS_SINCE_UNIX_EPOCH={:?}",
Duration::from_secs(u64::from_str_radix(BUILD_SECONDS_SINCE_UNIX_EPOCH, 10).unwrap())
);
println!("{:?}", matches);
Ok(())
let config_file = matches.value_of("config_file").unwrap();
match matches.subcommand() {
("init", Some(matches)) => {
let json_rpc_url = matches.value_of("json_rpc_url").unwrap();
let update_pubkey = matches
.value_of("update_pubkey")
.unwrap()
.parse::<Pubkey>()
.unwrap();
let data_dir = matches.value_of("data_dir").unwrap();
command::init(config_file, data_dir, json_rpc_url, &update_pubkey)
}
("info", Some(matches)) => {
let local_info_only = matches.is_present("local_info_only");
command::info(config_file, local_info_only)
}
("deploy", Some(matches)) => {
let download_url = matches.value_of("download_url").unwrap();
let update_manifest_keypair = matches.value_of("update_manifest_keypair").unwrap();
command::deploy(config_file, download_url, update_manifest_keypair)
}
("update", Some(_matches)) => command::update(config_file),
("run", Some(matches)) => {
let program_name = matches.value_of("program_name").unwrap();
let program_arguments = matches
.values_of("program_arguments")
.map(|iter| iter.collect())
.unwrap_or_else(|| vec![]);
command::run(config_file, program_name, program_arguments)
}
_ => unreachable!(),
}
}

View File

@ -0,0 +1,19 @@
use serde_derive::{Deserialize, Serialize};
use solana_sdk::signature::Signature;
/// Information required to download and apply a given update
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct UpdateManifest {
pub target: String, // Target triple (TARGET)
pub commit: String, // git sha1 of this update, must match the commit sha1 in the release tar.bz2
pub timestamp_secs: u64, // When the release was deployed in seconds since UNIX EPOCH
pub download_url: String, // Download URL to the release tar.bz2
pub download_signature: Signature, // Signature of the release tar.bz2 file, verify with the Account public key
}
/// Userdata of an Update Manifest program Account.
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct SignedUpdateManifest {
pub manifest: UpdateManifest,
pub manifest_signature: Signature, // Signature of UpdateInfo, verify with the Account public key
}