diff --git a/Cargo.lock b/Cargo.lock index 4119d70..273cb84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,7 @@ name = "firepilot" version = "1.1.0" dependencies = [ "doc-comment", - "firepilot_models 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "firepilot_models", "hyper", "hyperlocal", "log", @@ -169,19 +169,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "firepilot_models" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d31837f8c997263f1600fef39b579deb871538825b3dd13bbee95a539fe2658" -dependencies = [ - "serde", - "serde_derive", - "serde_json", - "url", - "uuid", -] - [[package]] name = "fnv" version = "1.0.7" diff --git a/firepilot/Cargo.toml b/firepilot/Cargo.toml index 57d0112..ef1f79a 100644 --- a/firepilot/Cargo.toml +++ b/firepilot/Cargo.toml @@ -23,7 +23,7 @@ hyperlocal = "0.8" serde_derive = "1.0.160" url = "^2.2" tokio = { version = "1.27.0", features = ["process", "rt", "macros"], default-features = false } -firepilot_models = "1.3.0" +firepilot_models = { path = "../firepilot_models" } tracing = "0.1" [dev-dependencies] diff --git a/firepilot/src/builder/mod.rs b/firepilot/src/builder/mod.rs index 97f1218..3e051df 100644 --- a/firepilot/src/builder/mod.rs +++ b/firepilot/src/builder/mod.rs @@ -51,17 +51,18 @@ //! ``` use crate::executor::Executor; -use firepilot_models::models::{BootSource, Drive, NetworkInterface}; +use firepilot_models::models::{BootSource, Drive, NetworkInterface, Vsock}; pub mod drive; pub mod executor; pub mod kernel; pub mod network_interface; +pub mod vsock; fn assert_not_none(key: &str, value: &Option) -> Result<(), BuilderError> { match value { Some(_) => Ok(()), - None => return Err(BuilderError::MissingRequiredField(key.to_string())), + None => Err(BuilderError::MissingRequiredField(key.to_string())), } } @@ -102,6 +103,7 @@ pub struct Configuration { pub kernel: Option, pub storage: Vec, pub interfaces: Vec, + pub vsock: Option, pub vm_id: String, } @@ -113,6 +115,7 @@ impl Configuration { executor: None, storage: Vec::new(), interfaces: Vec::new(), + vsock: None, vm_id, } } @@ -137,6 +140,11 @@ impl Configuration { self.interfaces.push(iface); self } + + pub fn with_vsock(mut self, vsock: Vsock) -> Configuration { + self.vsock = Some(vsock); + self + } } #[cfg(test)] diff --git a/firepilot/src/builder/vsock.rs b/firepilot/src/builder/vsock.rs new file mode 100644 index 0000000..3146841 --- /dev/null +++ b/firepilot/src/builder/vsock.rs @@ -0,0 +1,68 @@ +use crate::builder::{Builder, BuilderError}; +use firepilot_models::models::Vsock; + +use super::assert_not_none; + +#[derive(Debug)] +pub struct VsockBuilder { + pub guest_cid: Option, + pub uds_path: Option, +} + +impl Default for VsockBuilder { + fn default() -> Self { + Self::new() + } +} + +impl VsockBuilder { + pub fn new() -> VsockBuilder { + VsockBuilder { + guest_cid: None, + uds_path: None, + } + } + + pub fn with_guest_cid(mut self, guest_cid: i32) -> VsockBuilder { + self.guest_cid = Some(guest_cid); + self + } + + pub fn with_uds_path(mut self, uds_path: String) -> VsockBuilder { + self.uds_path = Some(uds_path); + self + } +} + +impl Builder for VsockBuilder { + fn try_build(self) -> Result { + assert_not_none(stringify!(self.guest_cid), &self.guest_cid)?; + assert_not_none(stringify!(self.uds_path), &self.uds_path)?; + Ok(Vsock { + guest_cid: self.guest_cid.unwrap(), + uds_path: self.uds_path.unwrap(), + vsock_id: None, + }) + } +} + +#[cfg(test)] +mod tests { + use crate::builder::vsock::VsockBuilder; + use crate::builder::Builder; + + #[test] + fn full_kernel() { + VsockBuilder::new() + .with_guest_cid(3) + .with_uds_path("/tmp/fc.sock".to_string()) + .try_build() + .unwrap(); + } + + #[test] + #[should_panic] + fn partial_kernel() { + VsockBuilder::new().with_guest_cid(3).try_build().unwrap(); + } +} diff --git a/firepilot/src/executor.rs b/firepilot/src/executor.rs index 72d3c38..155ef87 100644 --- a/firepilot/src/executor.rs +++ b/firepilot/src/executor.rs @@ -27,7 +27,7 @@ use tracing::{debug, error, info, instrument, trace}; use crate::machine::FirepilotError; use firepilot_models::models::vm::Vm; -use firepilot_models::models::{BootSource, Drive, NetworkInterface}; +use firepilot_models::models::{BootSource, Drive, NetworkInterface, Vsock}; /// Interface to determine how to execute commands on the socket and where to do it pub trait Execute { @@ -61,7 +61,7 @@ impl From for FirepilotError { fn from(e: ExecuteError) -> FirepilotError { match e { ExecuteError::CommandExecution(e) => FirepilotError::Setup(e), - ExecuteError::Request(url, e) => FirepilotError::Configure(format!("{}: {}", url, e)), + ExecuteError::Request(url, e) => FirepilotError::Configure(format!("{url}: {e}")), ExecuteError::Serialize(e) => FirepilotError::Configure(e.to_string()), ExecuteError::Socket(e) => FirepilotError::Configure(e), ExecuteError::WorkspaceCreation(e) => FirepilotError::Setup(e), @@ -102,6 +102,12 @@ pub struct Executor { id: String, } +impl Default for Executor { + fn default() -> Self { + Self::new() + } +} + impl Executor { /// Create a new Executor with no implementation, and with id "default" pub fn new() -> Executor { @@ -135,7 +141,7 @@ impl Executor { /// Return the configured executor, or panic if none is configured fn executor(&self) -> &dyn Execute { match &self.firecracker { - Some(firecracker) => return firecracker, + Some(firecracker) => firecracker, None => panic!("No executor found"), } } @@ -195,8 +201,7 @@ impl Executor { String::from_utf8(body.to_vec()).unwrap() ); return Err(ExecuteError::CommandExecution(format!( - "Failed to send request to {}, status: {}", - url, status + "Failed to send request to {url}, status: {status}" ))); } @@ -318,6 +323,19 @@ impl Executor { Ok(()) } + /// Apply vsock configuration on the VM + #[instrument(skip_all, fields(id = %self.id))] + pub async fn configure_vsock(&self, vsock: Vsock) -> Result<(), ExecuteError> { + debug!("Configure vsock"); + let json = serde_json::to_string(&vsock).map_err(ExecuteError::Serialize)?; + + let path = "/vsock"; + let url: hyper::Uri = Uri::new(self.chroot().join("firecracker.socket"), path).into(); + self.send_request(url, Method::PUT, json).await?; + + Ok(()) + } + /// Create needed folders where the VM will be configured #[instrument(skip(self), fields(id = %self.id))] pub fn create_workspace(&self) -> Result<(), ExecuteError> { diff --git a/firepilot/src/machine.rs b/firepilot/src/machine.rs index 379ba6e..e4dc364 100644 --- a/firepilot/src/machine.rs +++ b/firepilot/src/machine.rs @@ -133,6 +133,10 @@ impl Machine { self.executor.configure_drives(config.storage).await?; self.executor.configure_boot_source(kernel).await?; self.executor.configure_network(config.interfaces).await?; + if let Some(vsock) = config.vsock { + self.executor.configure_vsock(vsock).await?; + } + Ok(()) }