265 lines
10 KiB
Rust
265 lines
10 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, DeteeCliExt};
|
|
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::utils::{
|
|
deploy_new_app_and_update_config, fetch_config, override_envs_and_args_launch_config,
|
|
};
|
|
use crate::sgx::AppDeleteResponse;
|
|
use crate::sgx::{
|
|
append_uuid_list, get_app_node, get_app_node_by_contract, get_one_contract, inspect_node,
|
|
package_entry_from_name, print_nodes, write_uuid_list,
|
|
};
|
|
use crate::sgx::{AppContract, AppDeployResponse};
|
|
use crate::utils::block_on;
|
|
use crate::{cli_print, SimpleOutput};
|
|
use clap::ArgMatches;
|
|
use detee_shared::app_proto::ListAppContractsReq;
|
|
use detee_shared::sgx::types::brain::AppDeployConfig;
|
|
use detee_shared::sgx::types::brain::Resource;
|
|
|
|
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_uuid: 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_uuid, 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 (mut app_deploy_config, app_launch_config) = if let Some(file_path) =
|
|
deploy_match.get_one::<String>("yaml-path")
|
|
{
|
|
// TODO: maybe add launch config on deploy command with --launch-config flag
|
|
(AppDeployConfig::from_path(file_path).unwrap(), None)
|
|
} else {
|
|
let vcpus = *deploy_match.get_one::<u32>("vcpus").unwrap();
|
|
let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap();
|
|
let disk_size_gb = *deploy_match.get_one::<u32>("disk").unwrap();
|
|
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 node_unit_price = *deploy_match.get_one::<u64>("price").unwrap();
|
|
let location = deploy_match.get_one::<String>("location").unwrap().as_str();
|
|
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 private_package = false;
|
|
|
|
let resource = Resource { vcpus, memory_mb, disk_size_gb, port };
|
|
let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) {
|
|
Ok(node) => node.node_pubkey,
|
|
Err(e) => {
|
|
return Err(Box::new(std::io::Error::other(
|
|
format!("Could not get node pubkey due to error: {:?}", e).as_str(),
|
|
)));
|
|
}
|
|
};
|
|
|
|
let package_entry = package_entry_from_name(&package_name).unwrap();
|
|
let package_url = package_entry.package_url.clone();
|
|
let public_package_mr_enclave = Some(package_entry.mr_enclave.to_vec());
|
|
|
|
let config = block_on(fetch_config(&package_name))?;
|
|
|
|
let launch_config = override_envs_and_args_launch_config(config, envs, args);
|
|
|
|
(
|
|
AppDeployConfig {
|
|
package_url,
|
|
resource,
|
|
node_unit_price,
|
|
hours,
|
|
node_pubkey,
|
|
private_package,
|
|
app_name,
|
|
public_package_mr_enclave,
|
|
..Default::default()
|
|
},
|
|
Some(launch_config),
|
|
)
|
|
};
|
|
|
|
if app_deploy_config.app_name.is_empty() {
|
|
app_deploy_config.app_name = random_app_name();
|
|
}
|
|
|
|
let app_name = app_deploy_config.app_name.clone();
|
|
|
|
match block_on(deploy_new_app_and_update_config(app_deploy_config, app_launch_config)) {
|
|
Ok(new_app_res) if new_app_res.error.is_empty() => {
|
|
append_uuid_list(&new_app_res.uuid, &app_name)?;
|
|
Ok((new_app_res, app_name).into())
|
|
}
|
|
Ok(new_app_res) => Err(Box::new(std::io::Error::other(new_app_res.error))),
|
|
Err(e) => Err(Box::new(e)),
|
|
}
|
|
}
|
|
|
|
fn handle_inspect(inspect_match: &ArgMatches) {
|
|
let uuid: String = inspect_match.get_one::<String>("uuid").unwrap().clone();
|
|
if *inspect_match.get_one::<bool>("show-node").unwrap() {
|
|
cli_print(block_on(get_app_node_by_contract(&uuid)).map_err(Into::into));
|
|
} else {
|
|
cli_print(block_on(get_one_contract(&uuid)).map_err(Into::into))
|
|
}
|
|
}
|
|
|
|
fn handle_delete(
|
|
delete_match: &ArgMatches,
|
|
) -> Result<AppDeleteResponse, Box<dyn std::error::Error>> {
|
|
let app_uuid = delete_match
|
|
.get_one::<String>("uuid")
|
|
.ok_or_else(|| panic!("uuid argument required"))
|
|
.unwrap()
|
|
.to_owned();
|
|
|
|
match block_on(delete_app(app_uuid.clone())) {
|
|
Ok(_) => Ok(AppDeleteResponse {
|
|
uuid: app_uuid,
|
|
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_uuid_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(uuid)) =
|
|
(update_matche.get_one::<String>("config"), update_matche.get_one::<String>("uuid"))
|
|
{
|
|
let loaded_config = validate_yaml(file_path).unwrap();
|
|
match block_on(update_config(uuid, 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("uuid 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(uuid)) =
|
|
(get_matche.get_one::<String>("path"), get_matche.get_one::<String>("uuid"))
|
|
{
|
|
match block_on(get_config(uuid)) {
|
|
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 uuid arguments required")))
|
|
}
|
|
}
|