214 lines
6.4 KiB
Rust
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))
|
|
}
|