use super::config::SandboxConfig;
use super::error::SandboxErrorKind;
use super::sandbox::HyperlightSandbox;
use crate::tools::sandbox::traits::{Sandbox, SandboxFactory, SandboxFactoryFuture};
#[derive(Debug, Clone)]
pub struct HyperlightSandboxFactory {
config: SandboxConfig,
hypervisor_available: bool,
}
impl HyperlightSandboxFactory {
pub fn new() -> Result<Self, SandboxErrorKind> {
Self::with_config(SandboxConfig::default())
}
pub fn with_config(config: SandboxConfig) -> Result<Self, SandboxErrorKind> {
config.validate()?;
if !hyperlight_host::is_hypervisor_present() {
return Err(SandboxErrorKind::HypervisorNotAvailable);
}
Ok(Self {
config,
hypervisor_available: true,
})
}
#[must_use]
pub fn new_with_fallback() -> Self {
Self::with_config_fallback(SandboxConfig::default())
}
#[must_use]
pub fn with_config_fallback(config: SandboxConfig) -> Self {
let hypervisor_available =
hyperlight_host::is_hypervisor_present() && config.validate().is_ok();
Self {
config,
hypervisor_available,
}
}
#[must_use]
pub fn config(&self) -> &SandboxConfig {
&self.config
}
}
impl SandboxFactory for HyperlightSandboxFactory {
fn create(&self) -> SandboxFactoryFuture {
let config = self.config.clone();
let available = self.hypervisor_available;
Box::pin(async move {
if !available {
return Err(SandboxErrorKind::HypervisorNotAvailable.into());
}
let sandbox = HyperlightSandbox::new(config)?;
Ok(Box::new(sandbox) as Box<dyn Sandbox>)
})
}
fn is_available(&self) -> bool {
self.hypervisor_available
}
}
#[cfg(test)]
mod auto_factory {
use super::*;
use crate::tools::sandbox::stub::StubSandboxFactory;
#[derive(Debug, Clone)]
pub struct AutoSandboxFactory {
inner: AutoSandboxInner,
}
#[derive(Debug, Clone)]
enum AutoSandboxInner {
Hyperlight(HyperlightSandboxFactory),
Stub(StubSandboxFactory),
}
impl Default for AutoSandboxFactory {
fn default() -> Self {
Self::new()
}
}
impl AutoSandboxFactory {
#[must_use]
pub fn new() -> Self {
Self::with_config(SandboxConfig::default())
}
#[must_use]
pub fn with_config(config: SandboxConfig) -> Self {
let hyperlight_factory = HyperlightSandboxFactory::with_config(config.clone()).ok();
let inner = if let Some(factory) = hyperlight_factory {
match HyperlightSandbox::new(config) {
Ok(_sandbox) => {
tracing::info!("AutoSandboxFactory: Using Hyperlight sandboxing");
AutoSandboxInner::Hyperlight(factory)
}
Err(e) => {
tracing::warn!(
error = %e,
"AutoSandboxFactory: Hyperlight sandbox creation failed, falling back to stub"
);
AutoSandboxInner::Stub(StubSandboxFactory::new())
}
}
} else {
tracing::info!("AutoSandboxFactory: No hypervisor available, using stub");
AutoSandboxInner::Stub(StubSandboxFactory::new())
};
Self { inner }
}
#[must_use]
pub fn is_using_hyperlight(&self) -> bool {
matches!(self.inner, AutoSandboxInner::Hyperlight(_))
}
}
impl SandboxFactory for AutoSandboxFactory {
fn create(&self) -> SandboxFactoryFuture {
match &self.inner {
AutoSandboxInner::Hyperlight(factory) => factory.create(),
AutoSandboxInner::Stub(factory) => factory.create(),
}
}
fn is_available(&self) -> bool {
match &self.inner {
AutoSandboxInner::Hyperlight(factory) => factory.is_available(),
AutoSandboxInner::Stub(factory) => factory.is_available(),
}
}
}
}
#[cfg(test)]
pub use auto_factory::AutoSandboxFactory;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn factory_new_with_fallback_always_succeeds() {
let factory = HyperlightSandboxFactory::new_with_fallback();
assert!(format!("{:?}", factory).contains("HyperlightSandboxFactory"));
}
#[test]
fn factory_reports_availability() {
let factory = HyperlightSandboxFactory::new_with_fallback();
assert_eq!(
factory.is_available(),
hyperlight_host::is_hypervisor_present()
);
}
#[test]
fn factory_config_accessor() {
let config = SandboxConfig::new().with_memory_limit(100 * 1024 * 1024);
let factory = HyperlightSandboxFactory::with_config_fallback(config);
assert_eq!(factory.config().memory_limit, 100 * 1024 * 1024);
}
#[test]
fn auto_factory_default() {
let factory = AutoSandboxFactory::default();
assert!(format!("{:?}", factory).contains("AutoSandboxFactory"));
}
#[test]
fn auto_factory_is_available() {
let factory = AutoSandboxFactory::new();
assert!(factory.is_available());
}
#[test]
fn auto_factory_reports_hyperlight_usage() {
let factory = AutoSandboxFactory::new();
let _using_hyperlight = factory.is_using_hyperlight();
}
#[tokio::test]
async fn auto_factory_creates_sandbox() {
let factory = AutoSandboxFactory::new();
let result = factory.create().await;
assert!(result.is_ok(), "Factory create failed: {:?}", result.err());
let sandbox = result.unwrap();
assert!(sandbox.is_alive());
}
#[test]
#[ignore = "requires hypervisor"]
fn factory_new_requires_hypervisor() {
let result = HyperlightSandboxFactory::new();
if hyperlight_host::is_hypervisor_present() {
assert!(result.is_ok());
} else {
assert!(matches!(
result.unwrap_err(),
SandboxErrorKind::HypervisorNotAvailable
));
}
}
}