From bf8a4f3a8b8f28412cda6e306f42a26a69443310 Mon Sep 17 00:00:00 2001 From: Noor Date: Fri, 21 Mar 2025 21:24:04 +0530 Subject: [PATCH] feat: add app-node search tabled list of app nodes fix handler docs some todos --- src/bin/detee-cli.rs | 36 +++++++++++++++++++++++- src/sgx/cli_handler.rs | 16 +++++++++++ src/sgx/grpc_brain.rs | 21 ++++++++++++++ src/sgx/mod.rs | 64 ++++++++++++++++++++++++++++++++++-------- src/snp/cli_handler.rs | 2 +- src/snp/deploy.rs | 1 + 6 files changed, 127 insertions(+), 13 deletions(-) diff --git a/src/bin/detee-cli.rs b/src/bin/detee-cli.rs index 8790943..867b953 100644 --- a/src/bin/detee-cli.rs +++ b/src/bin/detee-cli.rs @@ -2,7 +2,7 @@ use clap::{builder::PossibleValue, Arg, Command}; use detee_cli::general::cli_handler::{ handle_account, handle_completion, handle_operators, handle_packagers, }; -use detee_cli::sgx::cli_handler::handle_app; +use detee_cli::sgx::cli_handler::{handle_app, handle_app_nodes}; use detee_cli::snp::cli_handler::{handle_vm, handle_vm_nodes}; use detee_cli::*; @@ -33,6 +33,7 @@ fn main() { match matches.subcommand() { Some(("completion", subcom_args)) => handle_completion(subcom_args, cmd), Some(("app", subcom_args)) => handle_app(subcom_args), + Some(("app-node", subcom_args)) => handle_app_nodes(subcom_args), Some(("vm", subcom_args)) => handle_vm(subcom_args), Some(("vm-node", subcom_args)) => handle_vm_nodes(subcom_args), Some(("operator", subcom_args)) => handle_operators(subcom_args), @@ -265,6 +266,39 @@ fn clap_cmd() -> Command { .long_about("List all the deployed apps in the enclave") ) ) + .subcommand(Command::new("app-node") + .about("info about Intel SGX servers registerd to DeTEE") + .subcommand(Command::new("search").about("search nodes based on filters")) + /* + .subcommand(Command::new("inspect").about("get detailed information about a node") + .arg( + Arg::new("ip") + .long("ip") + .help("main IP of the node your want to inspect") + .required(true) + ) + ) + .subcommand(Command::new("report").about("report a node for poor performance") + .arg( + Arg::new("pubkey") + .long("pubkey") + .help("public key of the node you are reporting") + .required(true) + ) + .arg( + Arg::new("contract") + .long("contract") + .help("UUID of the active contract with this node") + .required(true) + ) + .arg( + Arg::new("reason") + .long("reason") + .help("detail the performance issue you experienced") + ) + ) + */ + ) .subcommand(Command::new("vm") .about("virtual machines that run on AMD SEV-SNP nodes") .subcommand(Command::new("deploy").about("deploy a VM on the DeTEE network") diff --git a/src/sgx/cli_handler.rs b/src/sgx/cli_handler.rs index a458aa3..d01366a 100644 --- a/src/sgx/cli_handler.rs +++ b/src/sgx/cli_handler.rs @@ -26,6 +26,22 @@ pub fn handle_app(app_matche: &ArgMatches) { } } +pub fn handle_app_nodes(matches: &ArgMatches) { + match matches.subcommand() { + Some(("search", _)) => cli_print(crate::sgx::print_nodes().map_err(Into::into)), + Some(("inspect", _)) => todo!(), + Some(("report", _)) => { + // let node_pubkey: String = path_subcommand.get_one::("pubkey").unwrap().clone(); + // let contract_uuid: String = path_subcommand.get_one::("contract").unwrap().clone(); + // let reason: String = path_subcommand.get_one::("reason").unwrap().clone(); + todo!() + } + _ => { + println!("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::>(); diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs index 1d6b4a7..750940b 100644 --- a/src/sgx/grpc_brain.rs +++ b/src/sgx/grpc_brain.rs @@ -83,3 +83,24 @@ pub async fn get_one_app_node(req: AppNodeFilters) -> Result { Ok(res.into_inner()) } + +pub async fn get_app_node_list(req: AppNodeFilters) -> Result> { + log::debug!("Getting app nodes from brain..."); + let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?; + let mut nodes = Vec::new(); + let mut grpc_stream = daemon_serivce.list_app_nodes(sign_request(req)?).await?.into_inner(); + + while let Some(stream_update) = grpc_stream.next().await { + match stream_update { + Ok(node) => { + log::debug!("Received node from brain: {node:?}"); + nodes.push(node); + } + Err(e) => { + log::warn!("Received error instead of node list: {e:?}"); + } + } + } + log::debug!("Brain terminated list_nodes stream."); + Ok(nodes) +} diff --git a/src/sgx/mod.rs b/src/sgx/mod.rs index f2401e4..63be85a 100644 --- a/src/sgx/mod.rs +++ b/src/sgx/mod.rs @@ -1,3 +1,12 @@ +pub mod cli_handler; +pub mod config; +pub mod grpc_brain; +pub mod grpc_dtpm; +pub mod packaging; +pub mod utils; + +use crate::snp; +use crate::{constants::HRATLS_APP_PORT, utils::block_on}; use detee_shared::{ app_proto::{ AppContract as AppContractPB, AppNodeFilters, AppNodeListResp, AppResource, NewAppRes, @@ -5,19 +14,9 @@ use detee_shared::{ sgx::types::brain::Resource, }; use grpc_brain::get_one_app_node; - use serde::{Deserialize, Serialize}; use tabled::Tabled; -use crate::{constants::HRATLS_APP_PORT, utils::block_on}; - -pub mod cli_handler; -pub mod config; -pub mod grpc_brain; -pub mod grpc_dtpm; -pub mod packaging; -pub mod utils; - #[derive(Tabled, Debug, Serialize, Deserialize)] pub struct AppContract { #[tabled(rename = "Location")] @@ -150,7 +149,8 @@ impl crate::HumanOutput for AppDeleteResponse { pub async fn get_app_node( resource: Resource, - location: crate::snp::deploy::Location, + // TODO: change this to utils or something + location: snp::deploy::Location, ) -> Result { let app_node_filter = AppNodeFilters { vcpus: resource.vcpu, @@ -164,3 +164,45 @@ pub async fn get_app_node( }; get_one_app_node(app_node_filter).await } + +#[derive(Tabled, Debug, Serialize, Deserialize)] +pub struct TabledAppNode { + #[tabled(rename = "Operator")] + pub operator: String, + #[tabled(rename = "City, Region, Country")] + pub location: String, + #[tabled(rename = "IP")] + pub public_ip: String, + #[tabled(rename = "Price per unit")] + pub price: String, + #[tabled(rename = "Reports")] + pub reports: usize, +} + +impl From for TabledAppNode { + fn from(brain_node: AppNodeListResp) -> Self { + Self { + operator: brain_node.operator, + location: brain_node.city + ", " + &brain_node.region + ", " + &brain_node.country, + public_ip: brain_node.ip, + price: format!("{} nanoLP/min", brain_node.price), + reports: brain_node.reports.len(), + } + } +} + +impl super::HumanOutput for Vec { + fn human_cli_print(&self) { + let nodes: Vec = self.iter().map(|n| n.clone().into()).collect(); + let style = tabled::settings::Style::rounded(); + let mut table = tabled::Table::new(nodes); + table.with(style); + println!("{table}"); + } +} + +pub fn print_nodes() -> Result, grpc_brain::Error> { + log::debug!("This will support flags in the future, but we have only one node atm."); + let req = AppNodeFilters { ..Default::default() }; + Ok(block_on(grpc_brain::get_app_node_list(req))?) +} diff --git a/src/snp/cli_handler.rs b/src/snp/cli_handler.rs index f1592da..3fa0089 100644 --- a/src/snp/cli_handler.rs +++ b/src/snp/cli_handler.rs @@ -41,7 +41,7 @@ pub fn handle_vm_nodes(matches: &ArgMatches) { cli_print(general::report_node(node_pubkey, contract_uuid, reason).map_err(Into::into)) } _ => { - println!("Available commands are search and report. Use --help for more information.") + println!("Available commands are search, inspect and report. Use --help for more information.") } } } diff --git a/src/snp/deploy.rs b/src/snp/deploy.rs index a9b909b..29c7f85 100644 --- a/src/snp/deploy.rs +++ b/src/snp/deploy.rs @@ -13,6 +13,7 @@ pub enum IPv4Config { PublicIPv4, } +// TODO: push this out of snp module #[derive(Serialize, Deserialize, Default)] pub struct Location { pub node_ip: Option,