use std::path::PathBuf;
use std::time::Duration;
use arcbox_fc_driver::FcDriverConfig;
use arcbox_tap_net::Datapath;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdapterConfig {
pub binary: String,
#[serde(default)]
pub jailer: Option<JailerProcess>,
#[serde(default)]
pub log_level: Option<String>,
#[serde(default)]
pub no_seccomp: bool,
#[serde(default)]
pub seccomp_filter: Option<String>,
#[serde(default)]
pub http_api_max_payload_size: Option<usize>,
#[serde(default)]
pub mmds_size_limit: Option<usize>,
#[serde(default)]
pub socket_timeout_secs: Option<u64>,
#[serde(default)]
pub sandbox_datapath: Datapath,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JailerProcess {
pub binary: String,
#[serde(default)]
pub resource_limits: Vec<String>,
}
impl AdapterConfig {
pub fn from_toml(content: &str) -> Result<Self, toml::de::Error> {
#[derive(Deserialize)]
struct File {
firecracker: AdapterConfig,
}
Ok(toml::from_str::<File>(content)?.firecracker)
}
}
impl From<&AdapterConfig> for FcDriverConfig {
fn from(fc: &AdapterConfig) -> Self {
Self {
firecracker_binary: PathBuf::from(&fc.binary),
jailer_binary: fc.jailer.as_ref().map(|jc| PathBuf::from(&jc.binary)),
log_level: fc.log_level.clone(),
no_seccomp: fc.no_seccomp,
seccomp_filter: fc.seccomp_filter.as_deref().map(PathBuf::from),
http_api_max_payload_size: fc.http_api_max_payload_size,
mmds_size_limit: fc.mmds_size_limit,
socket_timeout: fc
.socket_timeout_secs
.map_or(Self::DEFAULT_SOCKET_TIMEOUT, Duration::from_secs),
resource_limits: fc
.jailer
.as_ref()
.map(|jc| jc.resource_limits.clone())
.unwrap_or_default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_driver_config_takes_the_process_flags_and_the_jailer_binary() {
let bare = AdapterConfig {
binary: "/usr/bin/firecracker".into(),
jailer: None,
log_level: None,
no_seccomp: false,
seccomp_filter: None,
http_api_max_payload_size: None,
mmds_size_limit: None,
socket_timeout_secs: None,
sandbox_datapath: Datapath::default(),
};
assert_eq!(
FcDriverConfig::from(&bare),
FcDriverConfig::new("/usr/bin/firecracker")
);
let full = AdapterConfig {
jailer: Some(JailerProcess {
binary: "/usr/bin/jailer".into(),
resource_limits: vec!["fsize=2048".into()],
}),
log_level: Some("Error".into()),
no_seccomp: true,
seccomp_filter: Some("/etc/fc/seccomp.bpf".into()),
http_api_max_payload_size: Some(1 << 20),
mmds_size_limit: Some(4096),
socket_timeout_secs: Some(15),
..bare
};
let driver = FcDriverConfig::from(&full);
assert_eq!(driver.jailer_binary, Some(PathBuf::from("/usr/bin/jailer")));
assert_eq!(driver.log_level.as_deref(), Some("Error"));
assert!(driver.no_seccomp);
assert_eq!(
driver.seccomp_filter,
Some(PathBuf::from("/etc/fc/seccomp.bpf"))
);
assert_eq!(driver.http_api_max_payload_size, Some(1 << 20));
assert_eq!(driver.mmds_size_limit, Some(4096));
assert_eq!(driver.socket_timeout, Duration::from_secs(15));
assert_eq!(driver.resource_limits, vec!["fsize=2048".to_string()]);
}
#[test]
fn the_datapath_defaults_to_ebpf_and_parses_the_fallback() {
assert_eq!(
AdapterConfig::from_toml("[firecracker]\nbinary = \"/fc\"\n")
.unwrap()
.sandbox_datapath,
Datapath::Ebpf
);
assert_eq!(
AdapterConfig::from_toml(
"[firecracker]\nbinary = \"/fc\"\nsandbox_datapath = \"filter\"\n"
)
.unwrap()
.sandbox_datapath,
Datapath::Filter
);
assert_eq!(
AdapterConfig::from_toml(
"[firecracker]\nbinary = \"/fc\"\nsandbox_datapath = \"iptables\"\n"
)
.unwrap()
.sandbox_datapath,
Datapath::Filter
);
}
#[test]
fn a_config_without_a_firecracker_binary_is_refused() {
assert!(AdapterConfig::from_toml("[firecracker]\ndata_dir = \"/d\"\n").is_err());
}
}