vmrunner
A utility to enable running tests, or components of integration tests specifically, in a micro-vm.
Host Requirements
mkosi - create images from a rootfs
libkrunfw - the linux kernel compiled as an .so / shared object
tap/tun - support for overlay networks in your kernel
The Magic
It takes std::env::current_exe() and uses a provided rootfs to execute the current process again inside the VM, mapping
the specific binary into the VM. To create an overlay network, we need to become root on the host, for which we use unshare.
A typical test uses #[vmrunner::test(...)] instead of #[test]. The macro builds a
TestSetup before calling the test body; VM harness code can then use the setup's per-test
system rootfs path when launching the guest.
use vmrunner::TestSetup;
#[vmrunner::test(system = "fedora")]
fn guest_smoke_test(setup: TestSetup) -> anyhow::Result<()> {
assert_eq!(setup.test_name(), "guest_smoke_test");
println!("guest rootfs: {}", setup.system_rootfs_path().display());
Ok(())
}
The generated wrapper is roughly equivalent to this expanded form:
use vmrunner::TestSetup;
fn __vmrunner_inner_guest_smoke_test(setup: TestSetup) -> anyhow::Result<()> {
assert_eq!(setup.test_name(), "guest_smoke_test");
println!("guest rootfs: {}", setup.system_rootfs_path().display());
Ok(())
}
#[test]
fn guest_smoke_test() -> anyhow::Result<()> {
if vmrunner::run_current_test_in_unshare_child("guest_smoke_test")
.expect("vmrunner failed to run test in user+network namespace")
{
return Ok(());
}
let vmrunner_setup = TestSetup::new_with_isolated_system_rootfs(
"guest_smoke_test",
std::env!("KRUN_TEST_ROOTFS"),
)
.expect("vmrunner failed to create per-test system rootfs state directory");
__vmrunner_inner_guest_smoke_test(vmrunner_setup)
}