diff --git a/.gitignore b/.gitignore index f73ca97..49e45fc 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,7 @@ $RECYCLE.BIN/ *.lnk # End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,vim,intellij+all,rust,rust-analyzer,visualstudiocode + +# RooFs and Kernel +kernel/* +rootfs/* \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 715dc56..94d73a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = [ "src/api","src/vmm", "src/cli"] +members = ["src/api", "src/vmm", "src/cli"] resolver = "2" diff --git a/proto/vmm.proto b/proto/vmm.proto new file mode 100644 index 0000000..1debb55 --- /dev/null +++ b/proto/vmm.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package vmmorchestrator; + +enum Language { + PYTHON = 0; + NODE = 1; + RUST = 2; +} + +enum LogLevel { + DEBUG = 0; + INFO = 1; + WARN = 2; + ERROR = 3; +} + +service VmmService { + rpc Run (RunVmmRequest) returns (RunVmmResponse); +} + +message RunVmmRequest { + + Language language = 1; + string code = 2; + string env = 3; + LogLevel log_level = 4; + +} + +message RunVmmResponse { +} diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index 088cf4d..5a6f3d0 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -8,3 +8,9 @@ edition = "2021" [dependencies] actix-web = "4.5.1" serde = "1.0.197" +tonic = "0.9" +prost = "0.11" +shared_models = { path="../shared-models" } + +[build-dependencies] +tonic-build = "0.9" \ No newline at end of file diff --git a/src/api/build.rs b/src/api/build.rs new file mode 100644 index 0000000..c53d705 --- /dev/null +++ b/src/api/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("../../proto/vmm.proto")?; + Ok(()) +} diff --git a/src/api/src/client.rs b/src/api/src/client.rs new file mode 100644 index 0000000..1abc188 --- /dev/null +++ b/src/api/src/client.rs @@ -0,0 +1,26 @@ +use tonic::transport::Channel; +use vmmorchestrator::vmm_service_client::VmmServiceClient; + +pub mod vmmorchestrator { + tonic::include_proto!("vmmorchestrator"); +} + +pub struct VmmClient { + client: VmmServiceClient, +} + +impl VmmClient { + pub async fn new() -> Result { + let client = VmmServiceClient::connect("http://[::1]:50051") + .await + .expect("Failed to connect to VMM service"); + + Ok(VmmClient { client }) + } + + pub async fn run_vmm(&mut self, request: vmmorchestrator::RunVmmRequest) { + let request = tonic::Request::new(request); + let response = self.client.run(request).await.unwrap(); + println!("RESPONSE={:?}", response); + } +} diff --git a/src/api/src/lib.rs b/src/api/src/lib.rs index 4e379ae..24f4aba 100644 --- a/src/api/src/lib.rs +++ b/src/api/src/lib.rs @@ -1 +1,2 @@ +pub mod client; pub mod services; diff --git a/src/api/src/main.rs b/src/api/src/main.rs index 162db20..2de501b 100644 --- a/src/api/src/main.rs +++ b/src/api/src/main.rs @@ -1,20 +1,13 @@ use actix_web::{App, HttpServer}; -use api::services::{configuration, logs, metrics, run, shutdown}; +use api::services::{run, shutdown}; #[actix_web::main] async fn main() -> std::io::Result<()> { let port = 3000; println!("Starting server on port: {}", port); - HttpServer::new(|| { - App::new() - .service(configuration) - .service(run) - .service(logs) - .service(metrics) - .service(shutdown) - }) - .bind(("127.0.0.1", port))? - .run() - .await + HttpServer::new(|| App::new().service(run).service(shutdown)) + .bind(("127.0.0.1", port))? + .run() + .await } diff --git a/src/api/src/services.rs b/src/api/src/services.rs index 97d724c..1648667 100644 --- a/src/api/src/services.rs +++ b/src/api/src/services.rs @@ -1,28 +1,29 @@ -use actix_web::{get, post, web, HttpResponse, Responder}; - -#[post("/configuration")] -pub async fn configuration(req_body: String) -> impl Responder { - // TODO: Use the body to create the vm configuration - HttpResponse::Ok().body(req_body) -} +use crate::client::vmmorchestrator::RunVmmRequest; +use crate::client::VmmClient; +use actix_web::{post, web, HttpResponse, Responder}; +use shared_models::CloudletDtoRequest; #[post("/run")] -pub async fn run(req_body: String) -> impl Responder { - // TODO: Use the body id to start the fm - HttpResponse::Ok().body(req_body) -} - -#[get("/logs/{id}")] -pub async fn logs(id: web::Path) -> HttpResponse { - // TODO: maybe not close the stream and keep sending the logs - HttpResponse::Ok().body(format!("Logs here: {}", &id)) -} +pub async fn run(req_body: web::Json) -> impl Responder { + let req = req_body.into_inner(); + let grpc_client = VmmClient::new().await; -#[get("/metrics/{id}")] -pub async fn metrics(id: web::Path) -> HttpResponse { - // TODO: Get the metrics for a VM with the given ID + let vmm_request = RunVmmRequest { + code: req.code, + env: req.env, + language: req.language as i32, + log_level: req.log_level as i32, + }; - HttpResponse::Ok().body(format!("Metrics here: {}", &id)) + match grpc_client { + Ok(mut client) => { + println!("Successfully connected to VMM service"); + client.run_vmm(vmm_request).await; + HttpResponse::Ok().body("Successfully ran VMM") + } + Err(e) => HttpResponse::InternalServerError() + .body("Failed to connect to VMM service with error: ".to_string() + &e.to_string()), + } } #[post("/shutdown")] diff --git a/src/cli/Cargo.toml b/src/cli/Cargo.toml index 89d519d..ef09404 100644 --- a/src/cli/Cargo.toml +++ b/src/cli/Cargo.toml @@ -14,3 +14,4 @@ serde_yaml = "0.9.34" schemars = "0.8.16" serde_json = "1.0.115" reqwest = "0.12.3" +shared_models = { path="../shared-models" } \ No newline at end of file diff --git a/src/cli/src/main.rs b/src/cli/src/main.rs index 1faebb8..2fa2760 100644 --- a/src/cli/src/main.rs +++ b/src/cli/src/main.rs @@ -1,15 +1,14 @@ use clap::Parser; use args::{CliArgs, Commands}; -use request::run_request; -use request::HttpRunRequest; +use shared_models::YamlClientConfigFile; + +use services::CloudletClient; use std::io::{self}; -use types::Config; -use utils::{load_config, read_file}; +use utils::ConfigFileHandler; mod args; -mod request; -mod types; +mod services; mod utils; #[tokio::main] @@ -18,20 +17,10 @@ async fn main() -> io::Result<()> { match args.command { Commands::Run { config_path } => { - let yaml_config: Config = - load_config(&config_path).expect("Error while loading the configuration file"); - - let code = - read_file(&yaml_config.code_path).expect("Error while reading the code file"); - println!("Code from file: \n{}", code); - - let env = - read_file(&yaml_config.env_path).expect("Error while reading the environment file"); - println!("Env from file : \n{}", env); - println!("Configuration from YAML file: \n {:#?}", yaml_config); - - let body = HttpRunRequest::new(yaml_config.language, env, code, yaml_config.log_level); - let response = run_request(body).await; + let yaml_config: YamlClientConfigFile = ConfigFileHandler::load_config(&config_path) + .expect("Error while loading the configuration file"); + let body = CloudletClient::new_cloudlet_config(yaml_config); + let response = CloudletClient::run(body).await; match response { Ok(_) => println!("Request successful"), diff --git a/src/cli/src/request.rs b/src/cli/src/request.rs deleted file mode 100644 index cb7872e..0000000 --- a/src/cli/src/request.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::types::{Language, LogLevel}; -use reqwest::Client; -use serde::Serialize; -use std::error::Error; - -#[derive(Serialize, Debug)] -pub struct HttpRunRequest { - pub language: Language, - pub env_content: String, - pub code_content: String, - pub log_level: LogLevel, -} - -impl HttpRunRequest { - pub fn new( - language: Language, - env_content: String, - code_content: String, - log_level: LogLevel, - ) -> Self { - HttpRunRequest { - language, - env_content, - code_content, - log_level, - } - } -} - -pub async fn run_request(request: HttpRunRequest) -> Result<(), Box> { - let client = Client::new(); - let res = client - .post("http://127.0.0.1:3000/run") - .body(serde_json::to_string(&request)?) - .send() - .await?; - println!("Response Status: {}", res.status()); - let body = res.text().await?; - println!("Response body: {}", body); - Ok(()) -} diff --git a/src/cli/src/services.rs b/src/cli/src/services.rs new file mode 100644 index 0000000..84835d2 --- /dev/null +++ b/src/cli/src/services.rs @@ -0,0 +1,39 @@ +use crate::utils::ConfigFileHandler; +use reqwest::Client; +use shared_models::{CloudletDtoRequest, YamlClientConfigFile}; +use std::error::Error; +pub struct CloudletClient {} + +impl CloudletClient { + pub fn new_cloudlet_config(config: YamlClientConfigFile) -> CloudletDtoRequest { + let code: String = ConfigFileHandler::read_file(&config.code_path) + .expect("Error while reading the code file"); + let env = ConfigFileHandler::read_file(&config.env_path) + .expect("Error while reading the environment file"); + let language = config.language; + let log_level = config.log_level; + CloudletDtoRequest { + language, + env, + code, + log_level, + } + } + + pub async fn run(request: CloudletDtoRequest) -> Result<(), Box> { + let client = Client::new(); + let json = serde_json::to_string(&request)?; + println!("REQUEST : {:?}", request); + let res = client + .post("http://127.0.0.1:3000/run") + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(json) + .send() + .await?; + + match res.status().as_u16() { + 200 => Ok(()), + _ => Err("Error while making the request".into()), + } + } +} diff --git a/src/cli/src/utils.rs b/src/cli/src/utils.rs index e9a1992..0d80519 100644 --- a/src/cli/src/utils.rs +++ b/src/cli/src/utils.rs @@ -1,20 +1,24 @@ -use crate::types::Config; +use shared_models::YamlClientConfigFile; use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; -pub fn load_config(config_path: &PathBuf) -> io::Result { - let mut file = File::open(config_path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - let config: Config = serde_yaml::from_str(&contents) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - Ok(config) -} +pub struct ConfigFileHandler {} + +impl ConfigFileHandler { + pub fn load_config(config_path: &PathBuf) -> io::Result { + let mut file = File::open(config_path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let config: YamlClientConfigFile = serde_yaml::from_str(&contents) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + Ok(config) + } -pub fn read_file(file_path: &str) -> io::Result { - let mut file = File::open(file_path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - Ok(contents) + pub fn read_file(file_path: &PathBuf) -> io::Result { + let mut file = File::open(file_path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + Ok(contents) + } } diff --git a/src/shared-models/Cargo.toml b/src/shared-models/Cargo.toml new file mode 100644 index 0000000..2422219 --- /dev/null +++ b/src/shared-models/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "shared_models" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.5.3", features = ["derive"] } +serde = { version = "1.0.197", features = ["derive"] } +serde_yaml = "0.9.34" +serde_json = "1.0.115" + +[lib] +name = "shared_models" +path = "src/lib.rs" \ No newline at end of file diff --git a/src/cli/src/types.rs b/src/shared-models/src/lib.rs similarity index 51% rename from src/cli/src/types.rs rename to src/shared-models/src/lib.rs index 7e6fac6..cfa7582 100644 --- a/src/cli/src/types.rs +++ b/src/shared-models/src/lib.rs @@ -1,27 +1,37 @@ +use std::path::PathBuf; + use clap::ValueEnum; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, ValueEnum, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum Language { - Rust, - Python, - Node, + RUST, + PYTHON, + NODE, } #[derive(Clone, Debug, ValueEnum, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum LogLevel { - Debug, - Info, - Warn, - Error, + DEBUG, + INFO, + WARN, + ERROR, } #[derive(Deserialize, Debug)] -pub struct Config { +pub struct YamlClientConfigFile { + pub language: Language, + pub env_path: PathBuf, + pub code_path: PathBuf, + pub log_level: LogLevel, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CloudletDtoRequest { pub language: Language, - pub env_path: String, - pub code_path: String, + pub env: String, + pub code: String, pub log_level: LogLevel, }