detee-cli/src/sgx/mod.rs
2025-03-22 01:32:06 +05:30

214 lines
6.4 KiB
Rust

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,
},
sgx::types::brain::Resource,
};
use grpc_brain::get_one_app_node;
use serde::{Deserialize, Serialize};
use tabled::Tabled;
#[derive(Tabled, Debug, Serialize, Deserialize)]
pub struct AppContract {
#[tabled(rename = "Location")]
pub location: String,
#[tabled(rename = "UUID")]
pub uuid: String,
pub name: String,
#[tabled(rename = "Cores")]
pub vcpu: u32,
#[tabled(rename = "Mem (MB)")]
pub memory_mb: u32,
#[tabled(rename = "Disk (MB)")]
pub disk_mb: u32,
#[tabled(rename = "LP/h")]
pub cost_h: String,
#[tabled(rename = "time left", display_with = "display_mins")]
pub time_left: u64,
#[tabled(rename = "Node IP")]
pub node_ip: String,
#[tabled(rename = "Exposed ports", display_with = "display_ports")]
pub exposed_host_ports: Vec<(u32, u32)>,
}
fn display_mins(minutes: &u64) -> String {
let mins = minutes % 60;
let hours = minutes / 60;
format!("{hours}h {mins}m")
}
fn display_ports(ports: &[(u32, u32)]) -> String {
ports.iter().map(|port| format!("({}:{})", port.0, port.1,)).collect::<Vec<_>>().join(", ")
}
impl crate::HumanOutput for Vec<AppContract> {
fn human_cli_print(&self) {
let style = tabled::settings::Style::rounded();
let mut table = tabled::Table::new(self);
table.with(style);
println!("{table}");
}
}
impl From<AppContractPB> for AppContract {
fn from(brain_app_contract: AppContractPB) -> Self {
let node_pubkey = brain_app_contract.node_pubkey.clone();
let location = match block_on(get_one_app_node(AppNodeFilters {
node_pubkey: node_pubkey.clone(),
..Default::default()
})) {
Ok(node) => format!("{}, {} ({})", node.city, node.region, node.country),
Err(e) => {
log::warn!("Could not get information about node {node_pubkey} fram brain: {e:?}");
String::new()
}
};
let AppResource { vcpu, memory_mb, disk_mb, .. } =
brain_app_contract.resource.unwrap_or_default();
let exposed_host_ports = brain_app_contract
.mapped_ports
.iter()
.map(|port| (port.host_port, port.app_port))
.collect::<Vec<_>>();
Self {
location,
uuid: brain_app_contract.uuid,
name: brain_app_contract.app_name,
vcpu,
memory_mb,
disk_mb,
cost_h: format!(
"{:.4}",
(brain_app_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0
),
time_left: brain_app_contract.locked_nano / brain_app_contract.nano_per_minute,
node_ip: brain_app_contract.public_ipv4,
exposed_host_ports,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AppDeployResponse {
pub status: String,
pub uuid: String,
pub name: String,
pub node_ip: String,
pub hratls_port: u32,
pub error: String,
}
impl crate::HumanOutput for AppDeployResponse {
fn human_cli_print(&self) {
println!("App deployd with UUID: {}", self.uuid);
}
}
impl From<NewAppRes> for AppDeployResponse {
fn from(value: NewAppRes) -> Self {
Self {
status: value.status,
uuid: value.uuid,
name: "".to_string(),
node_ip: value.ip_address,
hratls_port: value
.mapped_ports
.iter()
.find(|port| port.app_port == HRATLS_APP_PORT)
.map(|port| port.host_port)
.unwrap_or(HRATLS_APP_PORT),
error: value.error,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AppDeleteResponse {
pub uuid: String,
pub message: String,
}
impl crate::HumanOutput for AppDeleteResponse {
fn human_cli_print(&self) {
println!("App deleted successfully: UUID: {}", self.uuid);
}
}
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 {
vcpus: resource.vcpu,
memory_mb: resource.memory_mb,
storage_mb: resource.disk_mb,
country: location.country.clone().unwrap_or_default(),
region: location.region.clone().unwrap_or_default(),
city: location.city.clone().unwrap_or_default(),
ip: location.node_ip.clone().unwrap_or_default(),
node_pubkey: String::new(),
};
get_one_app_node(app_node_filter).await
}
pub fn inspect_node(ip: String) -> Result<AppNodeListResp, grpc_brain::Error> {
let req = AppNodeFilters { ip, ..Default::default() };
block_on(get_one_app_node(req))
}
#[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<AppNodeListResp> 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<AppNodeListResp> {
fn human_cli_print(&self) {
let nodes: Vec<TabledAppNode> = 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<Vec<AppNodeListResp>, grpc_brain::Error> {
log::debug!("This will support flags in the future, but we have only one node atm.");
let req = AppNodeFilters { ..Default::default() };
block_on(grpc_brain::get_app_node_list(req))
}