use anyhow::{anyhow, Result}; use detee_shared::sgx::types::brain::AppDeployConfig; use detee_shared::sgx::types::brain::Resource as ResourceConfig; use std::collections::HashSet; use std::fs::File; use std::io::Write; use serde::{Deserialize, Serialize}; use crate::container::delete_enclave; use crate::container::deploy_enclave; use crate::utils::handle_package; use crate::utils::prepare_port_map; use crate::HostConfig; use crate::global::APP_CONFIG_DIR; use crate::global::APP_NAME_PREFIX; use crate::global::USED_RESOURCES_PATH; #[derive(Debug, Default, Deserialize, Serialize)] pub struct HostResources { pub existing_apps: HashSet, pub reserved_vcpus: u32, pub reserved_memory_mb: u32, pub reserved_disk_mb: u32, pub reserved_host_ports: HashSet, } impl HostResources { pub fn new() -> Self { // TODO: compute host resources Self::default() } // TODO: implement load and save fn save_to_disk(&self) -> Result<()> { let mut file = File::create(&*USED_RESOURCES_PATH)?; file.write_all(serde_yml::to_string(self)?.as_bytes())?; Ok(()) } pub fn load_from_disk() -> Result { let content = std::fs::read_to_string(&*USED_RESOURCES_PATH)?; let res: Self = serde_yml::from_str(&content)?; Ok(res) } pub fn reserve_resources(&mut self, app: &App) -> Result<()> { self.reserved_memory_mb += app.app_resource.memory_mb; self.reserved_vcpus += app.app_resource.vcpu; self.reserved_disk_mb += app.app_resource.disk_mb; for (port, _) in app.mapped_ports.iter() { self.reserved_host_ports.insert(*port); } self.existing_apps.insert(app.uuid.clone()); self.save_to_disk() } pub fn un_reserve_resources(&mut self, app: &App) -> Result<()> { self.reserved_memory_mb -= app.app_resource.memory_mb; self.reserved_vcpus -= app.app_resource.vcpu; self.reserved_disk_mb -= app.app_resource.disk_mb; for (port, _) in app.mapped_ports.iter() { self.reserved_host_ports.remove(port); } self.existing_apps.take(&app.uuid).ok_or_else(|| { log::error!("App \"{}\" not found", app.uuid); anyhow!("App \"{}\" not found", app.uuid) })?; self.save_to_disk() } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct App { pub uuid: String, pub name: String, pub package_path: String, pub status: String, pub admin_pubkey: String, pub app_resource: ResourceConfig, pub mapped_ports: Vec<(u16, u16)>, } impl App { pub async fn new( new_app_req: AppDeployConfig, host_config: &HostConfig, host_resource: &mut HostResources, ) -> Result { let app_uuid = new_app_req.uuid.clone(); if host_config.max_cores_per_app < new_app_req.resource.vcpu { return Err(anyhow!("too many vcpus for app")); } if host_config.max_vcpu_reservation < host_resource .reserved_vcpus .saturating_add(new_app_req.resource.vcpu) { return Err(anyhow!("vcpus not available")); } if host_config.max_mem_reservation_mb < host_resource .reserved_memory_mb .saturating_add(new_app_req.resource.memory_mb) { return Err(anyhow!("not enough memory available")); } if new_app_req.resource.disk_mb < 128 { return Err(anyhow!("disk too small")); } let package_url = new_app_req.package_url.clone(); let mapped_ports = prepare_port_map(new_app_req.resource.port.clone()).await; let app_name = format!("{APP_NAME_PREFIX}-{app_uuid}"); let unarchive_dir = handle_package(package_url, app_uuid.clone(), host_config.delete_archive).await?; let exit_code = deploy_enclave(&unarchive_dir, app_name.clone(), mapped_ports.clone()).await?; if exit_code != 0 { // TODO: cleanup unarchive_dir return Err(anyhow!("Failed to deploy enclave")); } let app_instance = Self { uuid: app_uuid, name: app_name, package_path: unarchive_dir, status: "running".to_string(), admin_pubkey: new_app_req.admin_pubkey, app_resource: new_app_req.resource, mapped_ports, }; app_instance.write_config()?; host_resource .existing_apps .insert(app_instance.uuid.clone()); host_resource.reserve_resources(&app_instance)?; Ok(app_instance) } fn write_config(&self) -> Result<()> { std::fs::create_dir_all(&*APP_CONFIG_DIR)?; let mut file = File::create(format!("{}{}.yaml", *APP_CONFIG_DIR, &self.uuid))?; file.write_all(serde_yml::to_string(self)?.as_bytes())?; Ok(()) } pub async fn delete_app(&self, host_resource: &mut HostResources) -> Result<()> { let container_name = format!("{APP_NAME_PREFIX}-{}", &self.uuid); delete_enclave(container_name)?; host_resource.un_reserve_resources(self)?; Ok(()) } }