feat: add app inspect command with show node

refactor sgx handler imports
enhanced app list
This commit is contained in:
Noor 2025-03-22 04:48:23 +05:30
parent b12c8ebb4b
commit 9b3bdba70d
Signed by: noormohammedb
GPG Key ID: D83EFB8B3B967146
6 changed files with 106 additions and 27 deletions

2
Cargo.lock generated

@ -1110,7 +1110,7 @@ dependencies = [
[[package]]
name = "detee-shared"
version = "0.1.0"
source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=main#70e83dd0e982eeb491212c4a9d265df0b148fe24"
source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=main#3024c00b8e1c93e70902793385b92bc0a8d1f26a"
dependencies = [
"base64",
"prost",

@ -204,6 +204,19 @@ fn clap_cmd() -> Command {
.action(clap::ArgAction::Append)
)
)
.subcommand(Command::new("inspect").about("list all available information about a App")
.arg(
Arg::new("uuid")
.help("supply the uuid of the App contract")
.required(true)
)
.arg(
Arg::new("show-node")
.long("show-node")
.help("inspect the node that is runnnig this App")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(
Command::new("delete")
.about("delete deployed app")

@ -1,18 +1,21 @@
use super::grpc_brain::list_apps;
use super::utils::deploy_new_app_and_update_config;
use super::{get_app_node, AppContract, AppDeployResponse};
use crate::sgx::utils::deploy_new_app_and_update_config;
use crate::config::Config;
use crate::name_generator::random_app_name;
use crate::sgx;
use crate::sgx::config::{validate_yaml, DeteeCliExt};
use crate::sgx::grpc_brain::delete_app;
use crate::sgx::grpc_brain::{delete_app, list_contracts};
use crate::sgx::grpc_dtpm::{attest_and_send_config, get_config_from_enclave};
use crate::sgx::packaging::package_enclave;
use crate::sgx::utils::{fetch_config_and_mr_enclave, override_envs_and_args_launch_config};
use crate::sgx::AppDeleteResponse;
use crate::sgx::{
get_app_node, get_app_node_by_contract, get_one_contract, inspect_node, print_nodes,
};
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;
@ -20,8 +23,9 @@ 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(("list", _)) => cli_print(handle_list()),
Some(("config", subcom_args)) => handle_config(subcom_args),
_ => println!("No valid subcommand provided. Use --help for more information."),
}
@ -29,10 +33,10 @@ pub fn handle_app(app_matche: &ArgMatches) {
pub fn handle_app_nodes(matches: &ArgMatches) {
match matches.subcommand() {
Some(("search", _)) => cli_print(sgx::print_nodes().map_err(Into::into)),
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(sgx::inspect_node(ip).map_err(Into::into));
cli_print(inspect_node(ip).map_err(Into::into));
}
Some(("report", _)) => {
// let node_pubkey: String = path_subcommand.get_one::<String>("pubkey").unwrap().clone();
@ -130,6 +134,15 @@ fn handle_deploy(
}
}
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>> {
@ -150,13 +163,13 @@ fn handle_delete(
}
}
fn handle_list(_: &ArgMatches) -> Result<Vec<crate::sgx::AppContract>, Box<dyn std::error::Error>> {
match block_on(list_apps()) {
Ok(app_contracts) => {
Ok(app_contracts.into_iter().map(AppContract::from).collect::<Vec<_>>())
}
Err(e) => Err(Box::new(e)),
}
fn handle_list() -> Result<Vec<AppContract>, Box<dyn std::error::Error>> {
let req =
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();
Ok(contracts)
}
fn handle_config(matches: &ArgMatches) {

@ -36,6 +36,30 @@ impl crate::HumanOutput for AppNodeListResp {
}
}
impl crate::HumanOutput for AppContract {
fn human_cli_print(&self) {
let app_resource = self.resource.clone().unwrap_or_default();
let mapped_ports = self
.mapped_ports
.clone()
.iter()
.map(|p| format!("({},{})", p.host_port, p.app_port))
.collect::<Vec<_>>()
.join(", ");
println!(
"The App {} has the UUID {}, and it runs on the node {}",
self.app_name, self.uuid, self.node_pubkey
);
println!("The app has mapped ports by the node are: {mapped_ports}");
println!(
"The App has {} vCPUS, {}MB of memory and a disk of {} MB.",
app_resource.vcpu, app_resource.memory_mb, app_resource.disk_mb
);
println!("You have locked {} nanoLP in the contract, that get collected at a rate of {} nanoLP per minute.",
self.locked_nano, self.nano_per_minute);
}
}
pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result<NewAppRes> {
let resource = app_deploy_config.clone().resource;
let mut req: NewAppReq = app_deploy_config.clone().into();
@ -66,12 +90,9 @@ pub async fn delete_app(app_uuid: String) -> Result<()> {
Ok(())
}
pub async fn list_apps() -> Result<Vec<AppContract>> {
let admin_pubkey = Config::get_detee_wallet().unwrap();
let list_req = ListAppContractsReq { admin_pubkey };
pub async fn list_contracts(req: ListAppContractsReq) -> Result<Vec<AppContract>> {
let mut daemon_serivce = BrainAppCliClient::connect(Config::get_brain_url()).await?;
let mut res_stream =
daemon_serivce.list_app_contracts(sign_request(list_req)?).await?.into_inner();
let mut res_stream = daemon_serivce.list_app_contracts(sign_request(req)?).await?.into_inner();
let mut app_contracts = vec![];

@ -5,11 +5,13 @@ pub mod grpc_dtpm;
pub mod packaging;
pub mod utils;
use crate::config::Config;
use crate::snp;
use crate::{constants::HRATLS_APP_PORT, utils::block_on};
use detee_shared::{
app_proto::{
AppContract as AppContractPB, AppNodeFilters, AppNodeListResp, AppResource, NewAppRes,
AppContract as AppContractPB, AppNodeFilters, AppNodeListResp, AppResource,
ListAppContractsReq, NewAppRes,
},
sgx::types::brain::Resource,
};
@ -17,6 +19,16 @@ use grpc_brain::get_one_app_node;
use serde::{Deserialize, Serialize};
use tabled::Tabled;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Config(#[from] crate::config::Error),
#[error("Could not find a contract with the ID {0}")]
AppContractNotFound(String),
#[error("Brain returned the following error: {0}")]
Brain(#[from] grpc_brain::Error),
}
#[derive(Tabled, Debug, Serialize, Deserialize)]
pub struct AppContract {
#[tabled(rename = "Location")]
@ -101,6 +113,21 @@ impl From<AppContractPB> for AppContract {
}
}
pub async fn get_one_contract(uuid: &str) -> Result<AppContractPB, Error> {
let req = ListAppContractsReq {
admin_pubkey: Config::get_detee_wallet()?,
uuid: uuid.to_string(),
..Default::default()
};
let contracts = grpc_brain::list_contracts(req).await?;
if contracts.is_empty() {
return Err(Error::AppContractNotFound(uuid.to_string()));
}
// let _ = write_uuid_list(&contracts);
Ok(contracts[0].clone())
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AppDeployResponse {
pub status: String,
@ -149,7 +176,6 @@ impl crate::HumanOutput for AppDeleteResponse {
pub async fn get_app_node(
resource: Resource,
// TODO: change this to utils or something
location: snp::deploy::Location,
) -> Result<AppNodeListResp, grpc_brain::Error> {
let app_node_filter = AppNodeFilters {
@ -211,3 +237,9 @@ pub fn print_nodes() -> Result<Vec<AppNodeListResp>, grpc_brain::Error> {
let req = AppNodeFilters { ..Default::default() };
block_on(grpc_brain::get_app_node_list(req))
}
pub async fn get_app_node_by_contract(uuid: &str) -> Result<AppNodeListResp, Error> {
let contract = get_one_contract(uuid).await?;
Ok(get_one_app_node(AppNodeFilters { node_pubkey: contract.node_pubkey, ..Default::default() })
.await?)
}

@ -1,6 +1,6 @@
use super::grpc_brain::new_app;
use crate::constants::HRATLS_APP_PORT;
use crate::sgx::grpc_brain::list_apps;
use crate::sgx::get_one_contract;
use crate::sgx::grpc_dtpm::attest_and_send_config;
use detee_shared::app_proto::NewAppRes;
use detee_shared::sgx::types::brain::AppDeployConfig;
@ -27,9 +27,8 @@ pub enum Error {
}
pub async fn hratls_url_and_mr_enclave_from_app_id(app_id: &str) -> (String, Option<[u8; 32]>) {
let app_contracts = list_apps().await.expect("Could not get App contracts");
let app_contract = app_contracts.iter().find(|contract| contract.uuid == app_id);
if app_contract.is_none() {
let app_contract = get_one_contract(app_id).await;
if app_contract.is_err() {
eprintln!("Could not find App contract with ID: {}", app_id);
std::process::exit(1);
}
@ -98,6 +97,7 @@ pub fn calculate_nanolp_for_app(
let total_units =
(vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_mb as f64 / 10000f64);
let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64;
// TODO: change all println to eprintln
println!(
"Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).",
node_price as f64 / 1_000_000_000.0,