Buck2hubBuck2hub

Quick Start

Getting Started with Qlean

Welcome to Qlean!

Qlean is a system-level isolation testing library based on QEMU/KVM, providing complete virtual machine isolation environments for Rust projects.

Overview

Qlean provides a comprehensive testing solution for projects requiring system-level isolation by launching lightweight virtual machines during tests. It addresses two major challenges:

1. Complete Resource Isolation

Many projects require root privileges or direct manipulation of system-level resources. Traditional single-machine tests can easily crash the host system if tests fail. Qlean uses virtual machine isolation to completely isolate these operations within the VM, ensuring host system stability.

2. Convenient Multi-Machine Testing

For projects requiring multi-machine collaboration, Qlean provides a simple API that allows you to easily create and manage multiple VM instances in test code without complex infrastructure configuration.

Key Features

  • 🔒 Complete Isolation: Based on QEMU, providing full virtual machine isolation
  • 🔄 Multi-Machine Support: Easily create and manage multiple virtual machines
  • 🛡️ RAII-style Interface: Automatic resource management ensures VMs are properly cleaned up
  • 📦 Out-of-the-Box: Automated image downloading and extraction, no manual configuration needed
  • 🚀 KVM Acceleration: Hardware acceleration enabled by default for near-bare-metal performance, with automatic fallback to QEMU's TCG when KVM is unavailable to ensure compatibility

System Prerequisites

Qlean requires proper host setup, including installing QEMU and libvirt, and configuring KVM support. The following section outlines the essential system prerequisites. If you are using a Debian- or Ubuntu-based system, refer to the detailed setup guide for step-by-step host configuration instructions.

Install CLI utils

Before using Qlean, ensure that QEMU, guestfish, libvirt, libguestfs-tools and some other utils are properly installed on your Linux host. You can verify the installation with the following commands:

qemu-system-x86_64 --version
qemu-img --version
virsh --version
guestfish --version
virt-copy-out --version
xorriso --version
sha256sum --version
sha512sum --version

Configure qemu-bridge-helper

Qlean uses qemu-bridge-helper to manage networking for multiple virtual machines, so it requires proper configuration.

The CAP_NET_ADMIN capability needs to be set on for the default network helper:

sudo chmod u-s /usr/lib/qemu/qemu-bridge-helper
sudo setcap cap_net_admin+ep /usr/lib/qemu/qemu-bridge-helper

The ACL mechanism enforced by qemu-bridge-helper defaults to blacklisting all users, so the qlbr0 bridge created by qlean must be explicitly allowed:

sudo mkdir -p /etc/qemu
sudo sh -c 'echo "allow qlbr0" > /etc/qemu/bridge.conf'
sudo chmod 644 /etc/qemu/bridge.conf

Getting Started

Add the dependency to your Cargo.toml:

[dev-dependencies]
qlean = "0.2"
tokio = { version = "1", features = ["full"] }

Basic Example

Here's a simple test example with single machine:

use anyhow::Result;
use qlean::{Distro, MachineConfig, create_image, with_machine};

#[tokio::test]
async fn test_with_vm() -> Result<()> {
    // Create VM image and config
    let image = create_image(Distro::Debian, "debian-13-generic-amd64").await?;
    let config = MachineConfig::default();

    // Execute tests in the virtual machine
    with_machine(&image, &config, |vm| {
        Box::pin(async {
            // Execute a command
            let result = vm.exec("whoami").await?;
            assert!(result.status.success());
            assert_eq!(str::from_utf8(&result.stdout)?.trim(), "root");
            
            Ok(())
        })
    })
    .await?;

    Ok(())
}

The following is another example of a multi-machine test:

use anyhow::Result;
use qlean::{Distro, MachineConfig, create_image, with_pool};

#[tokio::test]
async fn test_ping() -> Result<()> {
    with_pool(|pool| {
        Box::pin(async {
            // Create VM image and config
            let image = create_image(Distro::Debian, "debian-13-generic-amd64").await?;
            let config = MachineConfig::default();

            // Add machines to the pool and initialize them concurrently
            pool.add("alice".to_string(), &image, &config).await?;
            pool.add("bob".to_string(), &image, &config).await?;
            pool.init_all().await?;

            // Get mutable references to both machines by name
            let mut alice = pool.get("alice").await.expect("Alice machine not found");
            let mut bob = pool.get("bob").await.expect("Bob machine not found");

            // Test ping from Alice to Bob and vice versa
            let alice_ip = alice.get_ip().await?;
            let result = bob.exec(format!("ping -c 4 {}", alice_ip)).await?;
            assert!(result.status.success());
            let bob_ip = bob.get_ip().await?;
            let result = alice.exec(format!("ping -c 4 {}", bob_ip)).await?;
            assert!(result.status.success());

            Ok(())
        })
    })
    .await?;

    Ok(())
}

For more examples, please refer to the tests directory.

Repos using Qlean

  • rk8s-dev/rk8s: A lightweight Kubernetes-compatible container orchestration system written in Rust.