use std::sync::Arc;
use crate::{
firewall::Firewall, jailer::Jailer, network::NetworkManager, shutdown::ShutdownConfig,
vsock::VsockManager,
};
#[derive(Clone)]
pub struct FirecrackerComposer {
pub network: Option<Arc<NetworkManager>>,
pub vsock: Option<Arc<VsockManager>>,
pub firewall: Option<Arc<Firewall>>,
pub jailer: Option<Arc<Jailer>>,
pub capture_console: bool,
pub graceful_shutdown: bool,
pub shutdown_config: ShutdownConfig,
}
impl Default for FirecrackerComposer {
fn default() -> Self {
Self::bare()
}
}
impl std::fmt::Debug for FirecrackerComposer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FirecrackerComposer")
.field("network", &self.network.is_some())
.field("vsock", &self.vsock.is_some())
.field("firewall", &self.firewall.is_some())
.field("jailer", &self.jailer.is_some())
.field("capture_console", &self.capture_console)
.field("graceful_shutdown", &self.graceful_shutdown)
.finish()
}
}
impl FirecrackerComposer {
pub fn bare() -> Self {
Self {
network: None,
vsock: None,
firewall: None,
jailer: None,
capture_console: false,
graceful_shutdown: false,
shutdown_config: ShutdownConfig::default(),
}
}
pub fn all() -> Self {
Self {
network: Some(Arc::new(NetworkManager::from_env())),
vsock: Some(Arc::new(VsockManager::from_env())),
firewall: Some(Arc::new(Firewall::from_env())),
jailer: Some(Arc::new(Jailer::from_env())),
capture_console: true,
graceful_shutdown: true,
shutdown_config: ShutdownConfig::default(),
}
}
pub fn from_env() -> Self {
Self {
network: opt_in("MICROVM_COMPOSE_NETWORK")
.then(|| Arc::new(NetworkManager::from_env())),
vsock: opt_in("MICROVM_COMPOSE_VSOCK").then(|| Arc::new(VsockManager::from_env())),
firewall: opt_in("MICROVM_COMPOSE_FIREWALL").then(|| Arc::new(Firewall::from_env())),
jailer: opt_in("MICROVM_COMPOSE_JAILER").then(|| Arc::new(Jailer::from_env())),
capture_console: opt_in("MICROVM_COMPOSE_CONSOLE"),
graceful_shutdown: opt_in("MICROVM_COMPOSE_GRACEFUL_SHUTDOWN"),
shutdown_config: ShutdownConfig::default(),
}
}
}
fn opt_in(var: &str) -> bool {
match std::env::var(var) {
Ok(v) => !matches!(v.trim(), "0" | "false" | "FALSE" | "False" | "no" | "NO"),
Err(_) => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bare_is_all_off() {
let c = FirecrackerComposer::bare();
assert!(c.network.is_none());
assert!(c.vsock.is_none());
assert!(c.firewall.is_none());
assert!(c.jailer.is_none());
assert!(!c.capture_console);
assert!(!c.graceful_shutdown);
}
#[test]
fn opt_in_default_is_true() {
let name = "MICROVM_COMPOSE_TEST_DEFAULT_ON";
unsafe { std::env::remove_var(name) };
assert!(opt_in(name));
}
#[test]
fn opt_in_explicit_zero_is_false() {
let name = "MICROVM_COMPOSE_TEST_ZERO";
unsafe { std::env::set_var(name, "0") };
assert!(!opt_in(name));
unsafe { std::env::remove_var(name) };
}
#[test]
fn opt_in_false_word_is_false() {
let name = "MICROVM_COMPOSE_TEST_FALSE";
unsafe { std::env::set_var(name, "false") };
assert!(!opt_in(name));
unsafe { std::env::set_var(name, "FALSE") };
assert!(!opt_in(name));
unsafe { std::env::remove_var(name) };
}
#[test]
fn opt_in_any_other_value_is_true() {
let name = "MICROVM_COMPOSE_TEST_TRUE";
unsafe { std::env::set_var(name, "1") };
assert!(opt_in(name));
unsafe { std::env::set_var(name, "yes") };
assert!(opt_in(name));
unsafe { std::env::set_var(name, "") };
assert!(opt_in(name));
unsafe { std::env::remove_var(name) };
}
}