mirror of https://github.com/aya-rs/aya
commit
f41db116ac
@ -0,0 +1,3 @@
|
||||
_results
|
||||
_tmp
|
||||
_images
|
@ -0,0 +1,42 @@
|
||||
Aya Regression Tests
|
||||
====================
|
||||
|
||||
The aya regression test suite is a set of tests to ensure that
|
||||
common usage behaviours work on real Linux distros
|
||||
## Prerequisites
|
||||
|
||||
This assumes you have a working Rust and Go toolchain on the host machine
|
||||
|
||||
1. `rustup toolcahin add x86_64-unknown-linux-musl`
|
||||
1. Install [`rtf`](https://github.com/linuxkit/rtf): `go install github.com/linuxkit/rtf@latest`
|
||||
1. Install rust-script: `cargo install rust-script`
|
||||
1. Install `qemu` and `cloud-init-utils` package - or any package that provides `cloud-localds`
|
||||
|
||||
It is not required, but the tests run significantly faster if you use `sccache`
|
||||
|
||||
## Usage
|
||||
|
||||
To read more about how to use `rtf`, see the [documentation](https://github.com/linuxkit/rtf/blob/master/docs/USER_GUIDE.md)
|
||||
|
||||
### Run the tests with verbose output
|
||||
|
||||
```
|
||||
rtf -vvv run
|
||||
```
|
||||
### Run the tests using an older kernel
|
||||
|
||||
```
|
||||
AYA_TEST_IMAGE=centos8 rtf -vvv run
|
||||
```
|
||||
|
||||
### Writing a test
|
||||
|
||||
Tests should follow this pattern:
|
||||
|
||||
- The eBPF code should be in a file named `${NAME}.ebpf.rs`
|
||||
- The userspace code should be in a file named `${NAME}.rs`
|
||||
- The userspace program should make assertions and exit with a non-zero return code to signal failure
|
||||
- VM start and stop is handled by the framework
|
||||
- Any files copied to the VM should be cleaned up afterwards
|
||||
|
||||
See `./cases` for examples
|
@ -0,0 +1,30 @@
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! aya-bpf = { path = "../../../../bpf/aya-bpf" }
|
||||
//! ```
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aya_bpf::{
|
||||
bindings::xdp_action,
|
||||
macros::xdp,
|
||||
programs::XdpContext,
|
||||
};
|
||||
|
||||
#[xdp(name="pass")]
|
||||
pub fn pass(ctx: XdpContext) -> u32 {
|
||||
match unsafe { try_pass(ctx) } {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => xdp_action::XDP_ABORTED,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn try_pass(_ctx: XdpContext) -> Result<u32, u32> {
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! aya = { path = "../../../../aya" }
|
||||
//! ```
|
||||
|
||||
use aya::{
|
||||
Bpf,
|
||||
programs::{Xdp, XdpFlags},
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
fn main() {
|
||||
println!("Loading XDP program");
|
||||
let mut bpf = Bpf::load_file("pass.o").unwrap();
|
||||
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
|
||||
dispatcher.load().unwrap();
|
||||
dispatcher.attach("eth0", XdpFlags::default()).unwrap();
|
||||
println!("Success...");
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
# SUMMARY: Check that a simple XDP program an be loaded
|
||||
# LABELS:
|
||||
|
||||
set -e
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
#. "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
NAME=pass
|
||||
|
||||
clean_up() {
|
||||
rm -rf ebpf user ${NAME}.o ${NAME}
|
||||
exec_vm rm -f pass pass.o
|
||||
}
|
||||
|
||||
trap clean_up EXIT
|
||||
|
||||
# Test code goes here
|
||||
compile_ebpf "$(pwd)/${NAME}.ebpf.rs"
|
||||
compile_user "$(pwd)/${NAME}.rs"
|
||||
|
||||
scp_vm pass.o
|
||||
scp_vm pass
|
||||
|
||||
exec_vm sudo ./pass
|
||||
|
||||
exit 0
|
@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# SUMMARY: Smoke tests to check that simple programs can be loaded on a VM
|
||||
# LABELS:
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
# . "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
set -e
|
||||
|
||||
group_init() {
|
||||
# Group initialisation code goes here
|
||||
return 0
|
||||
}
|
||||
|
||||
group_deinit() {
|
||||
# Group de-initialisation code goes here
|
||||
return 0
|
||||
}
|
||||
|
||||
CMD=$1
|
||||
case $CMD in
|
||||
init)
|
||||
group_init
|
||||
res=$?
|
||||
;;
|
||||
deinit)
|
||||
group_deinit
|
||||
res=$?
|
||||
;;
|
||||
*)
|
||||
res=1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $res
|
@ -0,0 +1,30 @@
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! aya-bpf = { path = "../../../../bpf/aya-bpf" }
|
||||
//! ```
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aya_bpf::{
|
||||
bindings::xdp_action,
|
||||
macros::xdp,
|
||||
programs::XdpContext,
|
||||
};
|
||||
|
||||
#[xdp(name="ihaveaverylongname")]
|
||||
pub fn pass(ctx: XdpContext) -> u32 {
|
||||
match unsafe { try_pass(ctx) } {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => xdp_action::XDP_ABORTED,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn try_pass(_ctx: XdpContext) -> Result<u32, u32> {
|
||||
Ok(xdp_action::XDP_PASS)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! aya = { path = "../../../../aya" }
|
||||
//! ```
|
||||
|
||||
use aya::{
|
||||
Bpf,
|
||||
programs::{Xdp, XdpFlags},
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::{thread, time};
|
||||
|
||||
fn main() {
|
||||
println!("Loading XDP program");
|
||||
let mut bpf = Bpf::load_file("name_test.o").unwrap();
|
||||
let dispatcher: &mut Xdp = bpf.program_mut("ihaveaverylongname").unwrap().try_into().unwrap();
|
||||
dispatcher.load().unwrap();
|
||||
dispatcher.attach("eth0", XdpFlags::default()).unwrap();
|
||||
thread::sleep(time::Duration::from_secs(20));
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# SUMMARY: Check that long names are properly truncated
|
||||
# LABELS:
|
||||
|
||||
set -e
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
#. "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
NAME=name_test
|
||||
|
||||
clean_up() {
|
||||
rm -rf ebpf user ${NAME}.o ${NAME}
|
||||
exec_vm sudo pkill -9 ${NAME}
|
||||
exec_vm rm ${NAME} ${NAME}.o
|
||||
}
|
||||
|
||||
trap clean_up EXIT
|
||||
|
||||
# Test code goes here
|
||||
compile_ebpf ${NAME}.ebpf.rs
|
||||
compile_user ${NAME}.rs
|
||||
|
||||
scp_vm ${NAME}.o
|
||||
scp_vm ${NAME}
|
||||
|
||||
exec_vm sudo ./${NAME}&
|
||||
prog_list=$(exec_vm sudo bpftool prog)
|
||||
echo "${prog_list}" | grep -q "xdp name ihaveaverylongn tag"
|
||||
|
||||
exit 0
|
@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# SUMMARY: Tests to check loader features
|
||||
# LABELS:
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
# . "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
set -e
|
||||
|
||||
group_init() {
|
||||
# Group initialisation code goes here
|
||||
return 0
|
||||
}
|
||||
|
||||
group_deinit() {
|
||||
# Group de-initialisation code goes here
|
||||
return 0
|
||||
}
|
||||
|
||||
CMD=$1
|
||||
case $CMD in
|
||||
init)
|
||||
group_init
|
||||
res=$?
|
||||
;;
|
||||
deinit)
|
||||
group_deinit
|
||||
res=$?
|
||||
;;
|
||||
*)
|
||||
res=1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $res
|
@ -0,0 +1,229 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Source the main regression test library if present
|
||||
[ -f "${RT_LIB}" ] && . "${RT_LIB}"
|
||||
|
||||
# Temporary directory for tests to use.
|
||||
AYA_TMPDIR="${RT_PROJECT_ROOT}/_tmp"
|
||||
|
||||
# Directory for VM images
|
||||
AYA_IMGDIR="${RT_PROJECT_ROOT}/_images"
|
||||
|
||||
# Test Architecture
|
||||
if [ -z "${AYA_TEST_ARCH}" ]; then
|
||||
AYA_TEST_ARCH="$(uname -m)"
|
||||
fi
|
||||
|
||||
# Test Image
|
||||
if [ -z "${AYA_TEST_IMAGE}" ]; then
|
||||
AYA_TEST_IMAGE="fedora35"
|
||||
fi
|
||||
|
||||
case "${AYA_TEST_IMAGE}" in
|
||||
fedora*) AYA_SSH_USER="fedora";;
|
||||
centos*) AYA_SSH_USER="centos";;
|
||||
esac
|
||||
|
||||
# compiles the ebpf program by using rust-script to create a temporary
|
||||
# cargo project in $(pwd)/ebpf. caller must add rm -rf ebpf to the clean_up
|
||||
# functionAYA_TEST_ARCH
|
||||
compile_ebpf() {
|
||||
file=$(basename "$1")
|
||||
dir=$(dirname "$1")
|
||||
base=$(echo "${file}" | cut -f1 -d '.')
|
||||
|
||||
rm -rf "${dir}/ebpf"
|
||||
|
||||
rust-script --pkg-path "${dir}/ebpf" --gen-pkg-only "$1"
|
||||
artifact=$(sed -n 's/^name = \"\(.*\)\"/\1/p' "${dir}/ebpf/Cargo.toml" | head -n1)
|
||||
|
||||
mkdir -p "${dir}/.cargo"
|
||||
cat > "${dir}/.cargo/config.toml" << EOF
|
||||
[build]
|
||||
target = "bpfel-unknown-none"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core"]
|
||||
EOF
|
||||
cat >> "${dir}/ebpf/Cargo.toml" << EOF
|
||||
[workspace]
|
||||
members = []
|
||||
EOF
|
||||
# overwrite the rs file as rust-script adds a main fn
|
||||
cp "$1" "${dir}/ebpf/${file}"
|
||||
cargo build -q --manifest-path "${dir}/ebpf/Cargo.toml"
|
||||
mv "${dir}/ebpf/target/bpfel-unknown-none/debug/${artifact}" "${dir}/${base}.o"
|
||||
rm -rf "${dir}/.cargo"
|
||||
}
|
||||
|
||||
# compiles the userspace program by using rust-script to create a temporary
|
||||
# cargo project in $(pwd)/user. caller must add rm -rf ebpf to the clean_up
|
||||
# function. this is required since the binary produced has to be run with
|
||||
# sudo to load an eBPF program
|
||||
compile_user() {
|
||||
file=$(basename "$1")
|
||||
dir=$(dirname "$1")
|
||||
base=$(echo "${file}" | cut -f1 -d '.')
|
||||
|
||||
rm -rf "${dir}/user"
|
||||
|
||||
rust-script --pkg-path "${dir}/user" --gen-pkg-only "$1"
|
||||
artifact=$(sed -n 's/^name = \"\(.*\)\"/\1/p' "${dir}/user/Cargo.toml" | head -n1)
|
||||
cat >> "${dir}/user/Cargo.toml" << EOF
|
||||
[workspace]
|
||||
members = []
|
||||
EOF
|
||||
cargo build -q --release --manifest-path "${dir}/user/Cargo.toml" --target=x86_64-unknown-linux-musl
|
||||
mv "${dir}/user/target/x86_64-unknown-linux-musl/release/${artifact}" "${dir}/${base}"
|
||||
}
|
||||
|
||||
download_images() {
|
||||
mkdir -p "${AYA_IMGDIR}"
|
||||
case $1 in
|
||||
fedora35)
|
||||
if [ ! -f "${AYA_IMGDIR}/fedora35.${AYA_TEST_ARCH}.qcow2" ]; then
|
||||
IMAGE="Fedora-Cloud-Base-35-1.2.${AYA_TEST_ARCH}.qcow2"
|
||||
IMAGE_URL="https://download.fedoraproject.org/pub/fedora/linux/releases/35/Cloud/${AYA_TEST_ARCH}/images"
|
||||
echo "Downloading: ${IMAGE}, this may take a while..."
|
||||
curl -o "${AYA_IMGDIR}/fedora35.${AYA_TEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
|
||||
fi
|
||||
;;
|
||||
centos8)
|
||||
if [ ! -f "${AYA_IMGDIR}/centos8.${AYA_TEST_ARCH}.qcow2" ]; then
|
||||
IMAGE="CentOS-8-GenericCloud-8.4.2105-20210603.0.${AYA_TEST_ARCH}.qcow2"
|
||||
IMAGE_URL="https://cloud.centos.org/centos/8/${AYA_TEST_ARCH}/images"
|
||||
echo "Downloading: ${IMAGE}, this may take a while..."
|
||||
curl -o "${AYA_IMGDIR}/centos8.${AYA_TEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "$1 is not a recognized image name"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
start_vm() {
|
||||
download_images "${AYA_TEST_IMAGE}"
|
||||
# prepare config
|
||||
cat > "${AYA_TMPDIR}/metadata.yaml" <<EOF
|
||||
instance-id: iid-local01
|
||||
local-hostname: test
|
||||
EOF
|
||||
|
||||
if [ ! -f "${AYA_TMPDIR}/test_rsa" ]; then
|
||||
ssh-keygen -t rsa -b 4096 -f "${AYA_TMPDIR}/test_rsa" -N "" -C "" -q
|
||||
pub_key=$(cat "${AYA_TMPDIR}/test_rsa.pub")
|
||||
cat > "${AYA_TMPDIR}/user-data.yaml" <<EOF
|
||||
#cloud-config
|
||||
ssh_authorized_keys:
|
||||
- ${pub_key}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ ! -f "${AYA_TMPDIR}/ssh_config" ]; then
|
||||
cat > "${AYA_TMPDIR}/ssh_config" <<EOF
|
||||
StrictHostKeyChecking=no
|
||||
UserKnownHostsFile=/dev/null
|
||||
GlobalKnownHostsFile=/dev/null
|
||||
EOF
|
||||
fi
|
||||
|
||||
cloud-localds "${AYA_TMPDIR}/seed.img" "${AYA_TMPDIR}/user-data.yaml" "${AYA_TMPDIR}/metadata.yaml"
|
||||
|
||||
case "${AYA_TEST_ARCH}" in
|
||||
x86_64)
|
||||
QEMU=qemu-system-x86_64
|
||||
machine="q35"
|
||||
cpu="qemu64"
|
||||
if [ "$(uname -m)" = "${AYA_TEST_ARCH}" ]; then
|
||||
if [ -c /dev/kvm ]; then
|
||||
machine="${machine},accel=kvm"
|
||||
cpu="host"
|
||||
elif [ "$(uname -s)" = "Darwin" ]; then
|
||||
machine="${machine},accel=hvf"
|
||||
cpu="host"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
aarch64)
|
||||
QEMU=qemu-system-aarch64
|
||||
machine="virt"
|
||||
cpu="cortex-a57"
|
||||
if [ "$(uname -m)" = "${AYA_TEST_ARCH}" ]; then
|
||||
if [ -c /dev/kvm ]; then
|
||||
machine="${machine},accel=kvm"
|
||||
cpu="host"
|
||||
elif [ "$(uname -s)" = "Darwin" ]; then
|
||||
machine="${machine},accel=hvf"
|
||||
cpu="host"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "${AYA_TEST_ARCH} is not supported"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
qemu-img create -F qcow2 -f qcow2 -o backing_file="${AYA_IMGDIR}/${AYA_TEST_IMAGE}.${AYA_TEST_ARCH}.qcow2" "${AYA_TMPDIR}/vm.qcow2" || return 1
|
||||
$QEMU \
|
||||
-machine "${machine}" \
|
||||
-cpu "${cpu}" \
|
||||
-m 2G \
|
||||
-display none \
|
||||
-monitor none \
|
||||
-daemonize \
|
||||
-pidfile "${AYA_TMPDIR}/vm.pid" \
|
||||
-device virtio-net-pci,netdev=net0 \
|
||||
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
|
||||
-drive if=virtio,format=qcow2,file="${AYA_TMPDIR}/vm.qcow2" \
|
||||
-drive if=virtio,format=raw,file="${AYA_TMPDIR}/seed.img" || return 1
|
||||
|
||||
trap cleanup_vm EXIT
|
||||
echo "Waiting for SSH on port 2222..."
|
||||
retry=0
|
||||
max_retries=300
|
||||
while ! ssh -q -F "${AYA_TMPDIR}/ssh_config" -o ConnectTimeout=1 -i "${AYA_TMPDIR}/test_rsa" ${AYA_SSH_USER}@localhost -p 2222 echo "Hello VM"; do
|
||||
retry=$((retry+1))
|
||||
if [ ${retry} -gt ${max_retries} ]; then
|
||||
echo "Unable to connect to VM"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "VM launched, installing dependencies"
|
||||
exec_vm sudo dnf install -qy bpftool
|
||||
}
|
||||
|
||||
scp_vm() {
|
||||
local=$1
|
||||
scp -q -F "${AYA_TMPDIR}/ssh_config" \
|
||||
-i "${AYA_TMPDIR}/test_rsa" \
|
||||
-P 2222 "${local}" \
|
||||
"${AYA_SSH_USER}@localhost:${local}"
|
||||
}
|
||||
|
||||
exec_vm() {
|
||||
ssh -q -F "${AYA_TMPDIR}/ssh_config" \
|
||||
-i "${AYA_TMPDIR}/test_rsa" \
|
||||
-p 2222 \
|
||||
${AYA_SSH_USER}@localhost \
|
||||
"$@"
|
||||
}
|
||||
|
||||
stop_vm() {
|
||||
if [ -f "${AYA_TMPDIR}/vm.pid" ]; then
|
||||
echo "Stopping VM forcefully"
|
||||
kill -9 "$(cat "${AYA_TMPDIR}/vm.pid")"
|
||||
rm "${AYA_TMPDIR}/vm.pid"
|
||||
fi
|
||||
rm -f "${AYA_TMPDIR}/vm.qcow2"
|
||||
}
|
||||
|
||||
cleanup_vm() {
|
||||
if [ "$?" != "0" ]; then
|
||||
stop_vm
|
||||
fi
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# NAME: aya
|
||||
# SUMMARY: Aya Regression Tests
|
||||
|
||||
# Source libraries. Uncomment if needed/defined
|
||||
# . "${RT_LIB}"
|
||||
. "${RT_PROJECT_ROOT}/_lib/lib.sh"
|
||||
|
||||
group_init() {
|
||||
# Group initialisation code goes here
|
||||
[ -r "${AYA_TMPDIR}" ] && rm -rf "${AYA_TMPDIR}"
|
||||
mkdir "${AYA_TMPDIR}"
|
||||
start_vm
|
||||
}
|
||||
|
||||
group_deinit() {
|
||||
# Group de-initialisation code goes here
|
||||
stop_vm
|
||||
}
|
||||
|
||||
CMD=$1
|
||||
case $CMD in
|
||||
init)
|
||||
group_init
|
||||
res=$?
|
||||
;;
|
||||
deinit)
|
||||
group_deinit
|
||||
res=$?
|
||||
;;
|
||||
*)
|
||||
res=1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $res
|
Loading…
Reference in New Issue