use super::config::{PoolConfig, SandboxConfig};
use super::error::SandboxErrorKind;
use super::pool::SandboxPool;
use acton_reactive::prelude::*;
#[derive(Debug, Clone)]
pub struct SandboxProvider {
config: SandboxConfig,
pool_config: PoolConfig,
}
impl SandboxProvider {
pub fn new(config: SandboxConfig) -> Result<Self, SandboxErrorKind> {
Self::with_pool_config(config, PoolConfig::default())
}
pub fn with_pool_config(
config: SandboxConfig,
pool_config: PoolConfig,
) -> Result<Self, SandboxErrorKind> {
Self::validate_platform()?;
config.validate()?;
pool_config.validate()?;
Ok(Self {
config,
pool_config,
})
}
fn validate_platform() -> Result<(), SandboxErrorKind> {
#[cfg(not(target_arch = "x86_64"))]
return Err(SandboxErrorKind::ArchitectureNotSupported {
arch: std::env::consts::ARCH.to_string(),
reason: "Hyperlight requires x86_64 with hardware virtualization".to_string(),
});
#[cfg(target_arch = "x86_64")]
{
if !hyperlight_host::is_hypervisor_present() {
return Err(SandboxErrorKind::HypervisorNotAvailable);
}
Ok(())
}
}
pub async fn spawn(&self, runtime: &mut ActorRuntime) -> ActorHandle {
SandboxPool::spawn(runtime, self.config.clone(), self.pool_config.clone()).await
}
#[must_use]
pub fn config(&self) -> &SandboxConfig {
&self.config
}
#[must_use]
pub fn pool_config(&self) -> &PoolConfig {
&self.pool_config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn provider_is_debug() {
let _type_check: fn(SandboxConfig) -> Result<SandboxProvider, SandboxErrorKind> =
SandboxProvider::new;
}
#[test]
fn provider_is_clone() {
fn assert_clone<T: Clone>() {}
assert_clone::<SandboxProvider>();
}
#[test]
fn validate_platform_returns_appropriate_error() {
let result = SandboxProvider::validate_platform();
#[cfg(not(target_arch = "x86_64"))]
assert!(matches!(
result,
Err(SandboxErrorKind::ArchitectureNotSupported { .. })
));
#[cfg(target_arch = "x86_64")]
{
match result {
Ok(()) => {
assert!(hyperlight_host::is_hypervisor_present());
}
Err(SandboxErrorKind::HypervisorNotAvailable) => {
assert!(!hyperlight_host::is_hypervisor_present());
}
Err(other) => panic!("unexpected error: {other}"),
}
}
}
#[test]
fn new_validates_config_after_platform() {
let config = SandboxConfig::new().with_memory_limit(100); let result = SandboxProvider::new(config);
assert!(result.is_err());
#[cfg(not(target_arch = "x86_64"))]
assert!(matches!(
result,
Err(SandboxErrorKind::ArchitectureNotSupported { .. })
));
}
#[test]
#[ignore = "requires hypervisor"]
fn new_with_valid_config_and_hypervisor() {
let config = SandboxConfig::default();
let expected_memory_limit = config.memory_limit;
let result = SandboxProvider::new(config);
if hyperlight_host::is_hypervisor_present() {
assert!(result.is_ok());
let provider = result.unwrap();
assert_eq!(provider.config().memory_limit, expected_memory_limit);
}
}
}