Skip to content
Merged
3 changes: 2 additions & 1 deletion .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ status-level = "pass"
# Profile for integration tests - run with limited parallelism due to QEMU/KVM resources
[profile.integration]
test-threads = 2
slow-timeout = { period = "500s", terminate-after = 60 }
# Full 20 minutes for a timeout by default, since GHA runners can be really slow
slow-timeout = { period = "1200s", terminate-after = 60 }
fail-fast = false
failure-output = "immediate"
success-output = "never"
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
branches: [ main ]
workflow_dispatch:

env:
# Something seems to be setting this in the default GHA runners, which breaks bcvk
# as the default runner user doesn't have access
LIBVIRT_DEFAULT_URI: "qemu:///session"


jobs:
build-and-test:
runs-on: ubuntu-24.04
Expand Down
13 changes: 13 additions & 0 deletions crates/integration-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@ pub(crate) fn run_bcvk(args: &[&str]) -> std::io::Result<CapturedOutput> {
run_command(&bck, args)
}

/// Run the bcvk command with inherited stdout/stderr (no capture)
/// Use this when you just need to verify the command succeeded without checking output
pub(crate) fn run_bcvk_nocapture(args: &[&str]) -> std::io::Result<()> {
let bck = get_bck_command().expect("Failed to get bcvk command");
let status = std::process::Command::new(&bck).args(args).status()?;
assert!(
status.success(),
"bcvk command failed with args: {:?}",
args
);
Ok(())
}

fn test_images_list() -> Result<()> {
println!("Running test: bcvk images list --json");

Expand Down
115 changes: 47 additions & 68 deletions crates/integration-tests/src/tests/libvirt_base_disks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@

use std::process::Command;

use crate::{get_bck_command, get_test_image};
use crate::{get_bck_command, get_test_image, run_bcvk};

/// Test that base disk is created and reused for multiple VMs
pub fn test_base_disk_creation_and_reuse() {
let bck = get_bck_command().unwrap();
let test_image = get_test_image();

// Generate unique names for test VMs
Expand All @@ -33,74 +32,60 @@ pub fn test_base_disk_creation_and_reuse() {

// Create first VM - this should create a new base disk
println!("Creating first VM (should create base disk)...");
let vm1_output = Command::new("timeout")
.args([
"300s",
&bck,
"libvirt",
"run",
"--name",
&vm1_name,
"--filesystem",
"ext4",
&test_image,
])
.output()
.expect("Failed to create first VM");

let vm1_stdout = String::from_utf8_lossy(&vm1_output.stdout);
let vm1_stderr = String::from_utf8_lossy(&vm1_output.stderr);

println!("VM1 stdout: {}", vm1_stdout);
println!("VM1 stderr: {}", vm1_stderr);

if !vm1_output.status.success() {
let vm1_output = run_bcvk(&[
"libvirt",
"run",
"--name",
&vm1_name,
"--filesystem",
"ext4",
&test_image,
])
.expect("Failed to create first VM");

println!("VM1 stdout: {}", vm1_output.stdout);
println!("VM1 stderr: {}", vm1_output.stderr);

if !vm1_output.success() {
cleanup_domain(&vm1_name);
cleanup_domain(&vm2_name);

panic!("Failed to create first VM: {}", vm1_stderr);
panic!("Failed to create first VM: {}", vm1_output.stderr);
}

// Verify base disk was created
assert!(
vm1_stdout.contains("Using base disk") || vm1_stdout.contains("base disk"),
vm1_output.stdout.contains("Using base disk") || vm1_output.stdout.contains("base disk"),
"Should mention base disk creation"
);

// Create second VM - this should reuse the base disk
println!("Creating second VM (should reuse base disk)...");
let vm2_output = Command::new("timeout")
.args([
"300s",
&bck,
"libvirt",
"run",
"--name",
&vm2_name,
"--filesystem",
"ext4",
&test_image,
])
.output()
.expect("Failed to create second VM");

let vm2_stdout = String::from_utf8_lossy(&vm2_output.stdout);
let vm2_stderr = String::from_utf8_lossy(&vm2_output.stderr);

println!("VM2 stdout: {}", vm2_stdout);
println!("VM2 stderr: {}", vm2_stderr);
let vm2_output = run_bcvk(&[
"libvirt",
"run",
"--name",
&vm2_name,
"--filesystem",
"ext4",
&test_image,
])
.expect("Failed to create second VM");

println!("VM2 stdout: {}", vm2_output.stdout);
println!("VM2 stderr: {}", vm2_output.stderr);

// Cleanup before assertions
cleanup_domain(&vm1_name);
cleanup_domain(&vm2_name);

if !vm2_output.status.success() {
panic!("Failed to create second VM: {}", vm2_stderr);
if !vm2_output.success() {
panic!("Failed to create second VM: {}", vm2_output.stderr);
}

// Verify base disk was reused (should be faster and mention using existing)
assert!(
vm2_stdout.contains("Using base disk") || vm2_stdout.contains("base disk"),
vm2_output.stdout.contains("Using base disk") || vm2_output.stdout.contains("base disk"),
"Should mention using base disk"
);

Expand Down Expand Up @@ -183,7 +168,6 @@ pub fn test_base_disks_prune_dry_run() {

/// Test that VM disks reference base disks correctly
pub fn test_vm_disk_references_base() {
let bck = get_bck_command().unwrap();
let test_image = get_test_image();

let timestamp = std::time::SystemTime::now()
Expand All @@ -197,26 +181,21 @@ pub fn test_vm_disk_references_base() {
cleanup_domain(&vm_name);

// Create VM
let output = Command::new("timeout")
.args([
"300s",
&bck,
"libvirt",
"run",
"--name",
&vm_name,
"--filesystem",
"ext4",
&test_image,
])
.output()
.expect("Failed to create VM");

if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let output = run_bcvk(&[
"libvirt",
"run",
"--name",
&vm_name,
"--filesystem",
"ext4",
&test_image,
])
.expect("Failed to create VM");

if !output.success() {
cleanup_domain(&vm_name);

panic!("Failed to create VM: {}", stderr);
panic!("Failed to create VM: {}", output.stderr);
}

// Get VM disk path from domain XML
Expand Down
Loading