diff --git a/src/agent/src/agents/mod.rs b/src/agent/src/agents/mod.rs new file mode 100644 index 0000000..92c9990 --- /dev/null +++ b/src/agent/src/agents/mod.rs @@ -0,0 +1,23 @@ +use crate::AgentResult; +use serde::Deserialize; + +pub mod rust; + +pub trait Agent { + fn prepare(&self) -> AgentResult<()>; + fn run(&self) -> AgentResult<()>; +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Language { + Rust, +} + +impl std::fmt::Display for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Language::Rust => write!(f, "rust"), + } + } +} diff --git a/src/agent/src/rust.rs b/src/agent/src/agents/rust.rs similarity index 80% rename from src/agent/src/rust.rs rename to src/agent/src/agents/rust.rs index 616864e..aee36ca 100644 --- a/src/agent/src/rust.rs +++ b/src/agent/src/agents/rust.rs @@ -1,9 +1,8 @@ -use std::{fs::create_dir_all, process::Command}; - +use super::Agent; +use crate::{workload, AgentResult}; use rand::distributions::{Alphanumeric, DistString}; use serde::Deserialize; - -use crate::{Agent, AgentConfig, AgentResult}; +use std::{fs::create_dir_all, process::Command}; #[derive(Deserialize)] #[serde(rename_all = "kebab-case")] @@ -18,7 +17,7 @@ struct RustAgentConfig { } pub struct RustAgent { - agent_config: AgentConfig, + workload_config: workload::config::Config, rust_config: RustAgentConfig, } @@ -43,12 +42,15 @@ impl RustAgent { Ok(std::str::from_utf8(&output.stdout).unwrap().to_string()) } } +} - pub fn new_from_config(agent_config: AgentConfig) -> Self { - let rust_config: RustAgentConfig = toml::from_str(&agent_config.config_string).unwrap(); +// TODO should change with a TryFrom +impl From for RustAgent { + fn from(workload_config: workload::config::Config) -> Self { + let rust_config: RustAgentConfig = toml::from_str(&workload_config.config_string).unwrap(); - RustAgent { - agent_config, + Self { + workload_config, rust_config, } } @@ -77,7 +79,7 @@ impl Agent for RustAgent { version = "0.1.0" edition = "2018" "#, - self.agent_config.workload_name + self.workload_config.workload_name ); std::fs::write(format!("{}/Cargo.toml", &function_dir), cargo_toml) @@ -91,17 +93,17 @@ impl Agent for RustAgent { let binary_path = match self.rust_config.build.release { true => format!( "{}/target/release/{}", - &function_dir, self.agent_config.workload_name + &function_dir, self.workload_config.workload_name ), false => format!( "{}/target/debug/{}", - &function_dir, self.agent_config.workload_name + &function_dir, self.workload_config.workload_name ), }; std::fs::copy( binary_path, - format!("/tmp/{}", self.agent_config.workload_name), + format!("/tmp/{}", self.workload_config.workload_name), ) .expect("Unable to copy binary"); @@ -111,7 +113,7 @@ impl Agent for RustAgent { } fn run(&self) -> AgentResult<()> { - let output = Command::new(format!("/tmp/{}", self.agent_config.workload_name)) + let output = Command::new(format!("/tmp/{}", self.workload_config.workload_name)) .output() .expect("Failed to run function"); diff --git a/src/agent/src/lib.rs b/src/agent/src/lib.rs index bd8c70f..6483143 100644 --- a/src/agent/src/lib.rs +++ b/src/agent/src/lib.rs @@ -1,37 +1,13 @@ -mod agent; -mod rust; - -use std::path::PathBuf; - -use agent::{Action, Agent, AgentConfig, AgentResult}; - -pub struct AgentRunner { - config: AgentConfig, - agent: Box, +mod agents; +pub mod workload { + pub mod config; + pub mod runner; } -impl AgentRunner { - pub fn new(config_path: String) -> Self { - let config = AgentConfig::new_from_file(&PathBuf::from(config_path)).unwrap(); - - let agent: Box = match config.language.as_str() { - "rust" => Box::new(rust::RustAgent::new_from_config(config.clone())), - _ => panic!("Unsupported language: {}", config.language), - }; - - AgentRunner { config, agent } - } - - pub fn run(&self) -> AgentResult<()> { - match self.config.action { - Action::Prepare => self.agent.prepare()?, - Action::Run => self.agent.run()?, - Action::PrepareAndRun => { - self.agent.prepare()?; - self.agent.run()?; - } - } - - Ok(()) - } +#[derive(Debug)] +pub enum AgentError { + OpenConfigFileError(std::io::Error), + ParseConfigError(toml::de::Error), } + +pub type AgentResult = Result; diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index 76f0a60..675a8fe 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -1,16 +1,18 @@ -use agent::AgentRunner; +use agent::workload::{config::Config, runner::Runner}; use clap::Parser; +use std::path::PathBuf; #[derive(Debug, Parser)] struct Args { #[clap(short, long, default_value = "/etc/cloudlet/agent/config.toml")] - config: String, + config: PathBuf, } fn main() { let args = Args::parse(); - let agent_runner = AgentRunner::new(args.config); + let config = Config::from_file(&args.config).unwrap(); + let runner = Runner::new(config); - agent_runner.run().unwrap(); + runner.run().unwrap(); } diff --git a/src/agent/src/agent.rs b/src/agent/src/workload/config.rs similarity index 56% rename from src/agent/src/agent.rs rename to src/agent/src/workload/config.rs index ff733e7..cf13934 100644 --- a/src/agent/src/agent.rs +++ b/src/agent/src/workload/config.rs @@ -1,38 +1,15 @@ -use std::path::PathBuf; - +use crate::{agents::Language, AgentError, AgentResult}; use serde::Deserialize; - -#[derive(Debug)] -pub enum AgentError { - OpenConfigFileError(std::io::Error), - ParseConfigError(toml::de::Error), -} - -pub type AgentResult = std::result::Result; - -pub trait Agent { - fn prepare(&self) -> AgentResult<()>; - fn run(&self) -> AgentResult<()>; -} - -#[derive(Debug, Clone, Deserialize)] -pub enum Action { - #[serde(rename = "prepare")] - Prepare, - #[serde(rename = "run")] - Run, - #[serde(rename = "prepare-and-run")] - PrepareAndRun, -} +use std::path::PathBuf; /// Generic agent configuration. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] -pub struct AgentConfig { +pub struct Config { /// Name of the worklod, used to identify the workload. pub workload_name: String, /// Language of the workload. - pub language: String, + pub language: Language, /// Action to perform. pub action: Action, /// Rest of the configuration as a string. @@ -40,11 +17,10 @@ pub struct AgentConfig { pub config_string: String, } -impl AgentConfig { - pub fn new_from_file(file_path: &PathBuf) -> AgentResult { +impl Config { + pub fn from_file(file_path: &PathBuf) -> AgentResult { let config = std::fs::read_to_string(file_path).map_err(AgentError::OpenConfigFileError)?; - let mut config: AgentConfig = - toml::from_str(&config).map_err(AgentError::ParseConfigError)?; + let mut config: Config = toml::from_str(&config).map_err(AgentError::ParseConfigError)?; let config_string = std::fs::read_to_string(file_path).map_err(AgentError::OpenConfigFileError)?; @@ -54,3 +30,11 @@ impl AgentConfig { Ok(config) } } + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Action { + Prepare, + Run, + PrepareAndRun, +} diff --git a/src/agent/src/workload/runner.rs b/src/agent/src/workload/runner.rs new file mode 100644 index 0000000..365c329 --- /dev/null +++ b/src/agent/src/workload/runner.rs @@ -0,0 +1,35 @@ +use super::config::{Action, Config}; +use crate::{ + agents::{rust, Agent, Language}, + AgentResult, +}; + +/// Runner for a workload. +/// Will execute the workload based on the inner agent (language). +pub struct Runner { + config: Config, + agent: Box, +} + +impl Runner { + pub fn new(config: Config) -> Self { + let agent: Box = match config.language { + Language::Rust => Box::new(rust::RustAgent::from(config.clone())), + }; + + Runner { config, agent } + } + + pub fn run(&self) -> AgentResult<()> { + match self.config.action { + Action::Prepare => self.agent.prepare()?, + Action::Run => self.agent.run()?, + Action::PrepareAndRun => { + self.agent.prepare()?; + self.agent.run()?; + } + } + + Ok(()) + } +}