mirror of https://github.com/aya-rs/aya
xtask: add kernel-test
This allows integration tests to be run using qemu at an arbitrary kernel version. Note that the integration tests which rely on tools like `bpftool` do not work because there is nothing in the image. The kernel-test task can be run like this to run the `smoke` integration test. ``` $ cargo xtask kernel-test --libbpf-dir $LIBBPF_DIR --kernel-version 5.19 -- smoke ```reviewable/pr638/r2
parent
5188b6d07f
commit
65079fe29b
@ -0,0 +1,152 @@
|
|||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use clap::Parser;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::{build_ebpf, integration_test};
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct Options {
|
||||||
|
/// Set the endianness of the BPF target
|
||||||
|
#[clap(default_value = "bpfel-unknown-none", long)]
|
||||||
|
pub bpf_target: build_ebpf::Architecture,
|
||||||
|
/// Build and run the release target
|
||||||
|
#[clap(long)]
|
||||||
|
pub release: bool,
|
||||||
|
/// libbpf directory
|
||||||
|
#[clap(long, action)]
|
||||||
|
pub libbpf_dir: String,
|
||||||
|
/// Kernel version to use.
|
||||||
|
#[clap(name = "kernel-version", long)]
|
||||||
|
pub kernel_version: String,
|
||||||
|
/// Path to the kerneltest root.
|
||||||
|
#[clap(name = "kerneltest-root", long, default_value = "kerneltest")]
|
||||||
|
pub kerneltest_root: String,
|
||||||
|
/// Arguments to pass to your application
|
||||||
|
#[clap(name = "args", last = true)]
|
||||||
|
pub run_args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MUSL_TARGET is the target triple for the musl libc
|
||||||
|
/// platform. It defaults to statically linking binaries.
|
||||||
|
/// We need a statically linked binary to run in the initramfs
|
||||||
|
/// created by bluebox.
|
||||||
|
const MUSL_TARGET: &str = "x86_64-unknown-linux-musl";
|
||||||
|
|
||||||
|
pub(crate) fn kernel_test(opts: Options) -> Result<(), anyhow::Error> {
|
||||||
|
let Options {
|
||||||
|
bpf_target,
|
||||||
|
release,
|
||||||
|
libbpf_dir,
|
||||||
|
run_args,
|
||||||
|
kernel_version,
|
||||||
|
kerneltest_root,
|
||||||
|
} = opts;
|
||||||
|
build_ebpf::build_ebpf(build_ebpf::BuildEbpfOptions {
|
||||||
|
target: bpf_target,
|
||||||
|
libbpf_dir: PathBuf::from(&libbpf_dir),
|
||||||
|
})
|
||||||
|
.context("failed to build ebpf")?;
|
||||||
|
let integration_test_bin = integration_test::build(integration_test::BuildOptions {
|
||||||
|
release,
|
||||||
|
target: Some(String::from(MUSL_TARGET)),
|
||||||
|
})?;
|
||||||
|
let kerneltest_root = PathBuf::from(&kerneltest_root);
|
||||||
|
let initramfs_path = build_initramfs(&kerneltest_root, &integration_test_bin, &run_args)?;
|
||||||
|
let kernel_image_path = get_kernel(&kernel_version, &kerneltest_root)?;
|
||||||
|
run(&initramfs_path, &kernel_image_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLUEBOX_BINARY: &str = "bluebox";
|
||||||
|
|
||||||
|
fn build_initramfs(
|
||||||
|
kerneltest_root: &Path,
|
||||||
|
integration_test_bin: &Path,
|
||||||
|
integration_test_args: &[String],
|
||||||
|
) -> Result<std::path::PathBuf, anyhow::Error> {
|
||||||
|
let integration_test_bin = integration_test_bin
|
||||||
|
.to_str()
|
||||||
|
.context("illegal path to integration test binary")?;
|
||||||
|
let args = match integration_test_args {
|
||||||
|
[] => String::new(),
|
||||||
|
_ => std::iter::once(":\"")
|
||||||
|
.chain(itertools::Itertools::intersperse(
|
||||||
|
integration_test_args.iter().map(|v| v.as_str()),
|
||||||
|
" ",
|
||||||
|
))
|
||||||
|
.chain(std::iter::once("\""))
|
||||||
|
.collect_vec()
|
||||||
|
.join(""),
|
||||||
|
};
|
||||||
|
let initramfs_path = PathBuf::from(kerneltest_root).join("initramfs.cpio");
|
||||||
|
which::which(BLUEBOX_BINARY)
|
||||||
|
.with_context(|| format!("{BLUEBOX_BINARY} not found"))
|
||||||
|
.context("try installing with `go install github.com/florianl/bluebox@latest`")?;
|
||||||
|
let args = format!("{integration_test_bin}{args}");
|
||||||
|
match Command::new(BLUEBOX_BINARY)
|
||||||
|
.arg("-e")
|
||||||
|
.arg(&args)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(&initramfs_path)
|
||||||
|
.status()
|
||||||
|
{
|
||||||
|
Err(err) => Err(anyhow::anyhow!("failed to build initramfs: {}", err)),
|
||||||
|
Ok(status) if !status.success() => Err(anyhow::anyhow!(
|
||||||
|
"failed to build initramfs: status code {}",
|
||||||
|
status
|
||||||
|
)),
|
||||||
|
Ok(_) => Ok(initramfs_path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_kernel(kernel_version: &str, kerneltest_root: &Path) -> Result<PathBuf, anyhow::Error> {
|
||||||
|
let kernel_name = format!("linux-{}.bz", kernel_version);
|
||||||
|
let image_path = PathBuf::from(kerneltest_root)
|
||||||
|
.join("kernels")
|
||||||
|
.join(kernel_name);
|
||||||
|
if image_path.exists() && image_path.is_file() {
|
||||||
|
return Ok(image_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tmp = tempfile::NamedTempFile::new().context("creating temp file for kernel image")?;
|
||||||
|
let url = format!(
|
||||||
|
"https://github.com/cilium/ci-kernels/raw/a15c0b2aa7cf32640c03764fa79b0a815608ddce/linux-{kernel_version}.bz"
|
||||||
|
);
|
||||||
|
|
||||||
|
reqwest::blocking::get(url)
|
||||||
|
.context("fetching kernel")?
|
||||||
|
.copy_to(&mut tmp)
|
||||||
|
.context("writing kernel file")?;
|
||||||
|
tmp.persist_noclobber(&image_path)
|
||||||
|
.context(format!("persisting kernel file {:?}", &image_path))?;
|
||||||
|
Ok(image_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const QEMU_BINARY: &str = "qemu-system-x86_64";
|
||||||
|
|
||||||
|
fn run(initramfs: &std::path::Path, kernel_image: &Path) -> Result<(), anyhow::Error> {
|
||||||
|
which::which(QEMU_BINARY).with_context(|| format!("{QEMU_BINARY} not found"))?;
|
||||||
|
let args = vec![
|
||||||
|
"-no-reboot",
|
||||||
|
"-append",
|
||||||
|
"printk.devkmsg=on kernel.panic=-1 crashkernel=256M",
|
||||||
|
"-kernel",
|
||||||
|
kernel_image.to_str().context("illegal kernel image path")?,
|
||||||
|
"-initrd",
|
||||||
|
initramfs.to_str().context("illegal initramfs path")?,
|
||||||
|
"-nographic",
|
||||||
|
"-append",
|
||||||
|
"console=ttyS0",
|
||||||
|
"-m",
|
||||||
|
"1.5G",
|
||||||
|
];
|
||||||
|
match Command::new(QEMU_BINARY).args(args).status() {
|
||||||
|
Err(err) => Err(anyhow::anyhow!("failed to run qemu: {}", err)),
|
||||||
|
Ok(status) if !status.success() => Err(anyhow::anyhow!("failed to run qemu: {}", status)),
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue