detee-cli/src/sgx/cli_handler.rs

218 lines
8.4 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::name_generator::random_app_name;
use crate::sgx::config::validate_yaml;
use crate::sgx::deploy::Reqwest;
use crate::sgx::grpc_brain::{delete_app, list_contracts};
use crate::sgx::grpc_dtpm::{get_config, update_config};
use crate::sgx::packaging::package_enclave;
use crate::sgx::{
get_app_node_by_contract, get_one_contract, inspect_node, print_nodes, write_app_id_list,
AppContract, AppDeleteResponse, AppDeployResponse,
};
use crate::utils::block_on;
use crate::{cli_print, SimpleOutput};
use clap::ArgMatches;
use detee_shared::app_proto::ListAppContractsReq;
pub fn handle_app(app_matche: &ArgMatches) {
match app_matche.subcommand() {
Some(("package", subcom_args)) => cli_print(handle_package(subcom_args)),
Some(("deploy", subcom_args)) => cli_print(handle_deploy(subcom_args)),
Some(("inspect", subcom_args)) => handle_inspect(subcom_args),
Some(("delete", subcom_args)) => cli_print(handle_delete(subcom_args)),
Some(("list", subcom_args)) => cli_print(handle_list(subcom_args)),
Some(("config", subcom_args)) => handle_config(subcom_args),
_ => eprintln!("No valid subcommand provided. Use --help for more information."),
}
}
pub fn handle_app_nodes(matches: &ArgMatches) {
match matches.subcommand() {
Some(("search", _)) => cli_print(print_nodes().map_err(Into::into)),
Some(("inspect", subcom_args)) => {
let ip: String = subcom_args.get_one::<String>("ip").unwrap().clone();
cli_print(inspect_node(ip).map_err(Into::into));
}
Some(("report", subcom_args)) => {
let node_pubkey: String = subcom_args.get_one::<String>("pubkey").unwrap().clone();
let contract_id: String = subcom_args.get_one::<String>("contract").unwrap().clone();
let reason: String = subcom_args.get_one::<String>("reason").unwrap().clone();
cli_print(
crate::general::report_node(node_pubkey, contract_id, reason).map_err(Into::into),
)
}
_ => {
eprintln!("Available commands are search, inspec and report. Use --help for more information.")
}
}
}
fn handle_package(package_match: &ArgMatches) -> Result<SimpleOutput, Box<dyn std::error::Error>> {
if let Some(file_paths) = package_match.get_many::<String>("files") {
let packaging_items = file_paths.cloned().collect::<Vec<String>>();
let package_type = package_match.get_one::<String>("package-type").unwrap().as_str();
match package_enclave(packaging_items, package_type) {
Ok(0) => Ok(SimpleOutput::from("Enclave packaged successfully")),
Ok(exit_code) => Err(Box::new(std::io::Error::other(format!(
"Enclave packaging failed with exit code: {exit_code}"
)))),
Err(e) => Err(Box::new(std::io::Error::other(format!(
"Could not package enclave due to error: {e}"
)))),
}
} else {
Err(Box::new(std::io::Error::other("files argument required")))
}
}
fn handle_deploy(
deploy_match: &ArgMatches,
) -> Result<AppDeployResponse, Box<dyn std::error::Error>> {
let vcpus = *deploy_match.get_one::<u32>("vcpus").unwrap();
let memory_mib = *deploy_match.get_one::<u32>("memory").unwrap();
let disk_size_mib = *deploy_match.get_one::<u32>("disk").unwrap() * 1024;
let port =
deploy_match.get_many::<u32>("port").unwrap_or_default().cloned().collect::<Vec<_>>();
let package_name = deploy_match.get_one::<String>("package").unwrap().clone();
let hours = *deploy_match.get_one::<u64>("hours").unwrap();
let price = *deploy_match.get_one::<u64>("price").unwrap();
let location = deploy_match.get_one::<String>("location").unwrap().clone();
let app_name = deploy_match.get_one::<String>("name").cloned().unwrap_or_else(random_app_name);
let envs =
deploy_match.get_many::<String>("env").unwrap_or_default().cloned().collect::<Vec<_>>();
let args =
deploy_match.get_many::<String>("arg").unwrap_or_default().cloned().collect::<Vec<_>>();
let app_deploy_config = Reqwest {
app_name,
package_name,
vcpus,
memory_mib,
disk_size_mib,
port,
hours,
location,
price,
envs,
args,
};
Ok(block_on(app_deploy_config.deploy())?)
}
fn handle_inspect(inspect_match: &ArgMatches) {
let app_id: String = inspect_match.get_one::<String>("id").unwrap().clone();
if *inspect_match.get_one::<bool>("show-node").unwrap() {
cli_print(block_on(get_app_node_by_contract(&app_id)).map_err(Into::into));
} else {
cli_print(block_on(get_one_contract(&app_id)).map_err(Into::into))
}
}
fn handle_delete(
delete_match: &ArgMatches,
) -> Result<AppDeleteResponse, Box<dyn std::error::Error>> {
let app_id = delete_match
.get_one::<String>("id")
.ok_or_else(|| panic!("app ID argument required"))
.unwrap()
.to_owned();
match block_on(delete_app(app_id.clone())) {
Ok(_) => Ok(AppDeleteResponse {
app_id,
message: "App deleted successfully".to_string(),
}),
Err(e) => {
Err(Box::new(std::io::Error::other(format!("Could not delete app due to error: {e}"))))
}
}
}
fn handle_list(list_matche: &ArgMatches) -> Result<Vec<AppContract>, Box<dyn std::error::Error>> {
let as_operator = *list_matche.get_one::<bool>("as-operator").unwrap_or(&false);
let req = if as_operator {
ListAppContractsReq {
admin_pubkey: Config::get_detee_wallet()?,
as_operator,
..Default::default()
}
} else {
ListAppContractsReq { admin_pubkey: Config::get_detee_wallet()?, ..Default::default() }
};
let contracts: Vec<AppContract> =
block_on(list_contracts(req))?.into_iter().map(|n| n.into()).collect();
write_app_id_list(&contracts)?;
Ok(contracts)
}
fn handle_config(matches: &ArgMatches) {
match matches.subcommand() {
Some(("validate", subcom_args)) => cli_print(handle_config_sub_validate(subcom_args)),
Some(("update", subcom_args)) => cli_print(handle_config_sub_update(subcom_args)),
Some(("get", subcom_args)) => cli_print(handle_config_sub_get(subcom_args)),
_ => {
eprintln!("No valid config subcommand provided.");
}
}
}
fn handle_config_sub_validate(
check_matche: &ArgMatches,
) -> Result<SimpleOutput, Box<dyn std::error::Error>> {
if let Some(file_path) = check_matche.get_one::<String>("config") {
let _config = validate_yaml(file_path);
Ok(SimpleOutput::from("The YAML file is valid!"))
} else {
Err(Box::new(std::io::Error::other("config file path argument required")))
}
}
fn handle_config_sub_update(
update_matche: &ArgMatches,
) -> Result<SimpleOutput, Box<dyn std::error::Error>> {
if let (Some(file_path), Some(app_id)) =
(update_matche.get_one::<String>("config"), update_matche.get_one::<String>("id"))
{
let loaded_config = validate_yaml(file_path).unwrap();
match block_on(update_config(app_id, loaded_config)) {
Ok(_) => Ok(SimpleOutput::from("App launch config updated successfully")),
Err(e) => Err(Box::new(std::io::Error::other(format!(
"Could not attest and update app launch config due to error: {e}"
)))),
}
} else {
Err(Box::new(std::io::Error::other("id and config arguments required")))
}
}
fn handle_config_sub_get(
get_matche: &ArgMatches,
) -> Result<SimpleOutput, Box<dyn std::error::Error>> {
if let (Some(file_path_to_save), Some(app_id)) =
(get_matche.get_one::<String>("path"), get_matche.get_one::<String>("id"))
{
match block_on(get_config(app_id)) {
Ok(config) => {
let config_yaml = serde_yaml::to_string(&config).unwrap();
std::fs::write(file_path_to_save, config_yaml).unwrap();
Ok(SimpleOutput::from(
format!("enclave config saved to: {file_path_to_save}").as_str(),
))
}
Err(e) => Err(Box::new(std::io::Error::other(format!(
"Could not get enclave config due to error: {e}"
)))),
}
} else {
Err(Box::new(std::io::Error::other("path and id arguments required")))
}
}