refactor actor model

implementing actor model inspire from snp daemon
migrated from grpc server model to grpc client model for
actor and brain integration
basic channel communication for actors and grpc
deleted unused proto file
This commit is contained in:
Noor 2025-01-29 18:08:59 +05:30
parent fd297488e2
commit 169bff5803
Signed by: noormohammedb
GPG Key ID: D83EFB8B3B967146
5 changed files with 183 additions and 160 deletions

4
Cargo.lock generated

@ -303,12 +303,14 @@ dependencies = [
"detee-shared", "detee-shared",
"env_logger", "env_logger",
"flate2", "flate2",
"log",
"prost", "prost",
"prost-types", "prost-types",
"rand", "rand",
"reqwest", "reqwest",
"tar", "tar",
"tokio", "tokio",
"tokio-stream",
"tonic", "tonic",
"tonic-build", "tonic-build",
] ]
@ -316,7 +318,7 @@ dependencies = [
[[package]] [[package]]
name = "detee-shared" name = "detee-shared"
version = "0.1.0" version = "0.1.0"
source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#78c84299947e887fe8d8c737656318f409c7f0b4" source = "git+ssh://git@gitea.detee.cloud/noormohammedb/detee-shared#3e783b11bab6894b6f98bd3c6ce44e8bf5b1f78b"
dependencies = [ dependencies = [
"base64", "base64",
"prost", "prost",

@ -9,15 +9,17 @@ prost = "0.13.4"
prost-types = "0.13.4" prost-types = "0.13.4"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] } tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
tonic = "0.12.3" tonic = "0.12.3"
detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared" }
# detee-shared = { path = "../detee-shared" }
chrono = "0.4.39" chrono = "0.4.39"
reqwest = "0.12.12" reqwest = "0.12.12"
flate2 = "1.0.35" flate2 = "1.0.35"
tar = "0.4.43" tar = "0.4.43"
anyhow = "1.0.95" anyhow = "1.0.95"
rand = "0.8.5" rand = "0.8.5"
tokio-stream = "0.1.17"
detee-shared = { git = "ssh://git@gitea.detee.cloud/noormohammedb/detee-shared" }
# detee-shared = { path = "../detee-shared" }
log = "0.4.25"
[build-dependencies] [build-dependencies]
tonic-build = "0.12.3" tonic-build = "0.12.3"

@ -1,19 +0,0 @@
syntax = "proto3";
package deamon;
message Empty {
}
message NewContainerReq {
repeated string port = 1;
}
message NewContainerRes {
string status = 1;
}
service DaemonService {
rpc CreateContainer (NewContainerReq) returns (NewContainerRes);
// rpc ListContainer (NodeFilters) returns (stream NodeListResp);
}

@ -1,141 +1,102 @@
use anyhow::Result; use anyhow::Result;
use detee_shared::pb::daemon::brain_sgx_daemon_client::BrainSgxDaemonClient;
use detee_shared::pb::daemon::daemon_message::Msg;
use detee_shared::pb::daemon::{BrainMessage, DaemonMessage};
use detee_shared::pb::shared::ContainerContracts;
use detee_shared::pb::shared::{Pubkey, RegisterNodeReq};
use tokio::sync::mpsc::Receiver;
use tokio::sync::mpsc::Sender;
use tokio::task::JoinSet;
use tokio_stream::wrappers::ReceiverStream;
use tokio_stream::StreamExt;
use tonic::transport::Channel;
use std::sync::Arc; pub struct ConnectionData {
use std::{net::SocketAddr, str::FromStr}; pub brain_url: String,
use tokio::sync::RwLock; pub brain_msg_tx: Sender<BrainMessage>,
use tonic::transport::Server; pub daemon_msg_rx: Receiver<DaemonMessage>,
pub daemon_msg_tx: Sender<DaemonMessage>,
use detee_shared::pb::daemon::daemon_service_server::{
DaemonService as DaemonServicePB, DaemonServiceServer as DaemonServiceServerPB,
};
use detee_shared::pb::daemon::{
ContainerFilters, ContainerInspectResp, ContainerListResp, DeleteContainerRes, NewContainerRes,
};
use detee_shared::pb::shared::Container as ContainerPB;
use detee_shared::types::shared::Container as ContainerConfig;
use crate::utils::handle_package;
use crate::DaemonState;
#[derive(Debug, Clone)]
pub struct DaemonServer {
pub data: Arc<RwLock<DaemonState>>,
} }
impl DaemonServer { pub async fn register_node(config: &crate::Config) -> Result<Vec<ContainerContracts>> {
pub fn new(data: Arc<RwLock<DaemonState>>) -> Self { let mut client = BrainSgxDaemonClient::connect(config.brain_url.clone()).await?;
Self { data }
log::debug!("registering node with brain");
let req = RegisterNodeReq {
..Default::default()
};
let mut container_contracts = vec![];
let mut grpc_stream = client.register_node(req).await?.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(contract) => {
container_contracts.push(contract);
} }
Err(e) => {
println!("Brain disconnected from register_node: {e}");
}
}
}
log::info!(
"Brain registration succcessful, with contract count: {}",
container_contracts.len()
);
pub async fn start(&self) -> Result<()> { Ok(container_contracts)
let port: String = std::env::var("PORT").unwrap_or_else(|_| "33400".to_string()); }
let addr = SocketAddr::from_str(format!("0.0.0.0:{port}").as_str())?; pub async fn connect_and_run(conn_data: ConnectionData) -> Result<()> {
let client = BrainSgxDaemonClient::connect(conn_data.brain_url).await?;
let daemon_server = DaemonServiceServerPB::new(DaemonServer::new(self.data.clone())); let mut streaming_tasks = JoinSet::new();
println!("Listening on {}", addr); streaming_tasks.spawn(receive_messages(client.clone(), conn_data.brain_msg_tx));
Server::builder() let task_output = streaming_tasks.join_next().await;
.add_service(daemon_server) println!("exiting: {task_output:?}");
.serve(addr)
.await?;
Ok(()) Ok(())
}
} }
#[tonic::async_trait] pub async fn receive_messages(
impl DaemonServicePB for DaemonServer { mut client: BrainSgxDaemonClient<Channel>,
async fn create_container( tx: Sender<BrainMessage>,
&self, ) -> Result<()> {
request: tonic::Request<ContainerPB>, let pubkey = "node_pubkey".to_owned();
) -> Result<tonic::Response<NewContainerRes>, tonic::Status> {
let req_data = request.into_inner();
if req_data.package_url.is_none() || req_data.resource.is_none() { log::debug!("starting to listen for messages from brain");
return Err(tonic::Status::data_loss("missing some data in request")); let mut grpc_stream = client.brain_messages(Pubkey { pubkey }).await?.into_inner();
while let Some(stream_update) = grpc_stream.next().await {
match stream_update {
Ok(msg) => {
log::info!("Received message from brain: {msg:?}");
let _ = tx.send(msg).await?;
} }
let unarchive_dir = handle_package(req_data.package_url.clone().unwrap_or_default()) Err(e) => {
.await println!("Brain disconnected from brain_messaages: {e}");
.map_err(|err| tonic::Status::internal(err.to_string()))?; }
}
}
println!("brain_messages is about to exit");
Ok(())
}
let req_container: ContainerConfig = req_data.into(); pub async fn send_messages(
let container_uuid = req_container.uuid.clone().unwrap_or_default().uuid; mut client: BrainSgxDaemonClient<Channel>,
rx: Receiver<DaemonMessage>,
tx: Sender<DaemonMessage>,
) -> Result<()> {
let pubkey = "node_pubkey".to_owned();
let mapped_ports = self let rx_stream = ReceiverStream::new(rx);
.data tx.send(DaemonMessage {
.write() msg: Some(Msg::Pubkey(Pubkey { pubkey })),
.await
.create_new_container(req_container, unarchive_dir)
.await
.map_err(|err| tonic::Status::internal(err.to_string()))?;
let mapped_ports = mapped_ports
.into_iter()
.map(|(host, container)| detee_shared::pb::shared::MappedPort {
host_port: host.into(),
container_port: container.into(),
}) })
.collect(); .await?;
client.daemon_messages(rx_stream).await?;
return Ok(tonic::Response::new(NewContainerRes { log::debug!("send_newvm_resp is about to exit");
container_id: Some(detee_shared::pb::shared::Uuid { Ok(())
uuid: container_uuid,
}),
status: "success".to_string(),
ip_address: "".to_string(),
mapped_ports,
}));
}
async fn delete_container(
&self,
req: tonic::Request<ContainerFilters>,
) -> Result<tonic::Response<DeleteContainerRes>, tonic::Status> {
let req_data = req.into_inner();
if req_data.container_id.is_none() {
return Err(tonic::Status::data_loss("missing container id"));
}
self.data
.write()
.await
.delete_container(
req_data.admin_pubkey,
req_data.container_id.unwrap_or_default().uuid,
)
.await
.map_err(|err| tonic::Status::internal(err.to_string()))?;
return Ok(tonic::Response::new(DeleteContainerRes {
..Default::default()
}));
}
async fn inspect_container(
&self,
req: tonic::Request<detee_shared::pb::shared::Uuid>,
) -> Result<tonic::Response<ContainerInspectResp>, tonic::Status> {
dbg!(req);
return Ok(tonic::Response::new(ContainerInspectResp {
..Default::default()
}));
}
// async fn container_log(
// &self,
// req: tonic::Request<detee_shared::pb::shared::Uuid>,
// ) -> Result<tonic::Response<Self::ContainerLogStream>, tonic::Status> {
// todo!()
// }
async fn list_containers(
&self,
req: tonic::Request<ContainerFilters>,
) -> Result<tonic::Response<ContainerListResp>, tonic::Status> {
dbg!(req);
return Ok(tonic::Response::new(ContainerListResp {
..Default::default()
}));
}
} }

@ -3,18 +3,95 @@ pub mod data;
pub mod grpc; pub mod grpc;
pub mod utils; pub mod utils;
pub use data::DaemonState; use std::time::Duration;
use std::sync::Arc;
use grpc::DaemonServer; pub use data::DaemonState;
use tokio::sync::RwLock; use detee_shared::pb::daemon::BrainMessage;
use detee_shared::pb::daemon::DaemonMessage;
use detee_shared::pb::shared::ContainerContracts;
use tokio::sync::mpsc::Receiver;
use tokio::sync::mpsc::Sender;
use tokio::time::sleep;
pub struct Config {
pub brain_url: String,
}
impl Default for Config {
fn default() -> Self {
let brain_url =
std::env::var("BRAIN_URL").unwrap_or_else(|_| "http://127.0.0.1:31337".to_string());
Self { brain_url }
}
}
pub struct ContainerHandler {
receiver: Receiver<BrainMessage>,
sender: Sender<DaemonMessage>,
config: Config,
// res: state::Resources,
}
impl ContainerHandler {
pub fn new(receiver: Receiver<BrainMessage>, sender: Sender<DaemonMessage>) -> Self {
Self {
receiver,
sender,
config: Config::default(),
}
}
fn handle_contracts(&mut self, contracts: Vec<ContainerContracts>) -> () {
dbg!(&contracts);
}
async fn run(mut self) -> () {
while let Some(brain_msg) = self.receiver.recv().await {
match brain_msg.msg {
Some(msg) => {
dbg!(&msg);
}
None => {
log::error!("Brain disconnected");
break;
}
}
}
}
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Detee daemon"); println!("Detee daemon");
DaemonServer::new(Arc::new(RwLock::new(DaemonState::default()))) loop {
.start() let (brain_msg_tx, brain_msg_rx) = tokio::sync::mpsc::channel(6);
.await?; let (daemon_msg_tx, daemon_msg_rx) = tokio::sync::mpsc::channel(6);
Ok(())
let mut container_handler = ContainerHandler::new(brain_msg_rx, daemon_msg_tx.clone());
let brain_url = container_handler.config.brain_url.clone();
match grpc::register_node(&container_handler.config).await {
Ok(container_contracts) => container_handler.handle_contracts(container_contracts),
Err(e) => log::error!("Failed to connect to brain: {e}"),
}
tokio::spawn(async move {
container_handler.run().await;
});
log::info!("Connecting to brain...");
if let Err(e) = grpc::connect_and_run(grpc::ConnectionData {
brain_url,
brain_msg_tx,
daemon_msg_rx,
daemon_msg_tx,
})
.await
{
log::error!("The connection broke: {e}");
}
sleep(Duration::from_secs(3)).await;
}
} }