use arcbox_engine::{VmBackend, host_nested_virt};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NestedVirtCapability {
pub supported: bool,
pub reason: String,
}
impl NestedVirtCapability {
fn supported() -> Self {
Self {
supported: true,
reason: String::new(),
}
}
fn unsupported(reason: impl Into<String>) -> Self {
Self {
supported: false,
reason: reason.into(),
}
}
}
#[must_use]
pub fn nested_virt_for_backend(backend: VmBackend) -> NestedVirtCapability {
if !backend.supports_nested_virt() {
return NestedVirtCapability::unsupported(format!(
"the {} backend does not support nested virtualization; switch to the VZ backend \
(`abctl system backend vz`)",
backend.as_str().to_uppercase()
));
}
let host = host_nested_virt();
if host.supported {
NestedVirtCapability::supported()
} else {
NestedVirtCapability::unsupported(host.reason)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_os = "macos")]
#[test]
fn a_backend_that_cannot_nest_is_refused_with_an_actionable_reason() {
let capability = nested_virt_for_backend(VmBackend::Hv);
assert!(!capability.supported);
assert!(
capability.reason.contains("VZ backend"),
"{}",
capability.reason
);
}
#[cfg(target_os = "macos")]
#[test]
fn a_nesting_backend_defers_to_the_hardware_probe() {
let capability = nested_virt_for_backend(VmBackend::Vz);
assert_eq!(capability.supported, host_nested_virt().supported);
assert_eq!(capability.supported, capability.reason.is_empty());
}
#[cfg(not(target_os = "macos"))]
#[test]
fn off_macos_every_backend_defers_to_the_hardware_probe() {
for backend in [VmBackend::Hv, VmBackend::Vz] {
let capability = nested_virt_for_backend(backend);
assert_eq!(capability.supported, host_nested_virt().supported);
assert_eq!(capability.supported, capability.reason.is_empty());
}
}
#[test]
fn the_reason_never_supplies_its_own_subject() {
for backend in [VmBackend::Hv, VmBackend::Vz] {
let reason = nested_virt_for_backend(backend).reason;
assert!(
!reason.to_lowercase().contains("sandbox"),
"reason must name the cause, not the subject: {reason}"
);
}
}
}