// 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::("ip").unwrap().clone(); cli_print(inspect_node(ip).map_err(Into::into)); } Some(("report", subcom_args)) => { let node_pubkey: String = subcom_args.get_one::("pubkey").unwrap().clone(); let contract_uuid: String = subcom_args.get_one::("contract").unwrap().clone(); let reason: String = subcom_args.get_one::("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> { if let Some(file_paths) = package_match.get_many::("files") { let packaging_items = file_paths.cloned().collect::>(); let package_type = package_match.get_one::("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> { let (mut app_deploy_config, app_launch_config) = if let Some(file_path) = deploy_match.get_one::("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::("vcpus").unwrap(); let memory_mb = *deploy_match.get_one::("memory").unwrap(); let disk_size_gb = *deploy_match.get_one::("disk").unwrap(); let port = deploy_match.get_many::("port").unwrap_or_default().cloned().collect::>(); let package_name = deploy_match.get_one::("package").unwrap().clone(); let hours = *deploy_match.get_one::("hours").unwrap(); let node_unit_price = *deploy_match.get_one::("price").unwrap(); let location = deploy_match.get_one::("location").unwrap().as_str(); let app_name = deploy_match.get_one::("name").cloned().unwrap_or_else(random_app_name); let envs = deploy_match.get_many::("env").unwrap_or_default().cloned().collect::>(); let args = deploy_match.get_many::("arg").unwrap_or_default().cloned().collect::>(); 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::("uuid").unwrap().clone(); if *inspect_match.get_one::("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> { let app_uuid = delete_match .get_one::("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, Box> { let as_operator = *list_matche.get_one::("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 = 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> { if let Some(file_path) = check_matche.get_one::("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> { if let (Some(file_path), Some(uuid)) = (update_matche.get_one::("config"), update_matche.get_one::("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> { if let (Some(file_path_to_save), Some(uuid)) = (get_matche.get_one::("path"), get_matche.get_one::("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"))) } }