vmtest: use libguestfs for disk image manipulations

Running vmtest inside a container removes the ability to use certain
root powers, among other things - mounting arbitrary images. Use
libguestfs in order to avoid having to mount anything.

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
This commit is contained in:
Ilya Leoshkevich
2021-03-30 00:36:50 +02:00
committed by Andrii Nakryiko
parent 3b1714aa92
commit 1987a34fc9
2 changed files with 91 additions and 53 deletions

View File

@@ -12,7 +12,7 @@ runs:
echo 'echo ::group::Env setup' > /tmp/ci_setup echo 'echo ::group::Env setup' > /tmp/ci_setup
echo export DEBIAN_FRONTEND=noninteractive >> /tmp/ci_setup echo export DEBIAN_FRONTEND=noninteractive >> /tmp/ci_setup
echo sudo apt-get update >> /tmp/ci_setup echo sudo apt-get update >> /tmp/ci_setup
echo sudo apt-get install -y aptitude qemu-kvm zstd binutils-dev elfutils libcap-dev libelf-dev libdw-dev >> /tmp/ci_setup echo sudo apt-get install -y aptitude qemu-kvm zstd binutils-dev elfutils libcap-dev libelf-dev libdw-dev libguestfs-tools >> /tmp/ci_setup
echo export PROJECT_NAME='libbpf' >> /tmp/ci_setup echo export PROJECT_NAME='libbpf' >> /tmp/ci_setup
echo export AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")" >> /tmp/ci_setup echo export AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")" >> /tmp/ci_setup
echo export REPO_ROOT=$GITHUB_WORKSPACE >> /tmp/ci_setup echo export REPO_ROOT=$GITHUB_WORKSPACE >> /tmp/ci_setup

View File

@@ -15,7 +15,7 @@ Run "${PROJECT_NAME}" tests in a virtual machine.
This exits with status 0 on success, 1 if the virtual machine ran successfully This exits with status 0 on success, 1 if the virtual machine ran successfully
but tests failed, and 2 if we encountered a fatal error. but tests failed, and 2 if we encountered a fatal error.
This script uses sudo to mount and modify the disk image. This script uses sudo to work around a libguestfs bug.
Arguments: Arguments:
IMG path of virtual machine disk image to create IMG path of virtual machine disk image to create
@@ -177,6 +177,11 @@ else
fi fi
IMG="${!OPTIND}" IMG="${!OPTIND}"
fi fi
if [[ "${SOURCE_FULLCOPY}" == "1" ]]; then
img_size=2G
else
img_size=8G
fi
unset URLS unset URLS
cache_urls() { cache_urls() {
@@ -242,15 +247,29 @@ cp_img() {
create_rootfs_img() { create_rootfs_img() {
local path="$1" local path="$1"
set_nocow "$path" set_nocow "$path"
truncate -s 2G "$path" truncate -s "$img_size" "$path"
mkfs.ext4 -q "$path" mkfs.ext4 -q "$path"
} }
download_rootfs() { download_rootfs() {
local rootfsversion="$1" local rootfsversion="$1"
local dir="$2"
download "${ARCH}/${PROJECT_NAME}-vmtest-rootfs-$rootfsversion.tar.zst" | download "${ARCH}/${PROJECT_NAME}-vmtest-rootfs-$rootfsversion.tar.zst" |
zstd -d | sudo tar -C "$dir" -x zstd -d
}
tar_in() {
local dst_path="$1"
# guestfish --remote does not forward file descriptors, which prevents
# us from using `tar-in -` or bash process substitution. We don't want
# to copy all the data into a temporary file, so use a FIFO.
tmp=$(mktemp -d)
mkfifo "$tmp/fifo"
cat >"$tmp/fifo" &
local cat_pid=$!
guestfish --remote tar-in "$tmp/fifo" "$dst_path"
wait "$cat_pid"
rm -r "$tmp"
tmp=
} }
if (( LIST )); then if (( LIST )); then
@@ -297,18 +316,12 @@ echo "Disk image: $IMG" >&2
tmp= tmp=
ARCH_DIR="$DIR/$ARCH" ARCH_DIR="$DIR/$ARCH"
mkdir -p "$ARCH_DIR" mkdir -p "$ARCH_DIR"
mnt="$(mktemp -d -p "$DIR" mnt.XXXXXXXXXX)"
cleanup() { cleanup() {
if [[ -n $tmp ]]; then if [[ -n $tmp ]]; then
rm -f "$tmp" || true rm -rf "$tmp" || true
fi
if mountpoint -q "$mnt"; then
sudo umount "$mnt" || true
fi
if [[ -d "$mnt" ]]; then
rmdir "$mnt" || true
fi fi
guestfish --remote exit 2>/dev/null || true
} }
trap cleanup EXIT trap cleanup EXIT
@@ -324,12 +337,19 @@ else
fi fi
fi fi
# Mount and set up the rootfs image. # Mount and set up the rootfs image. Use a persistent guestfish session in
# order to avoid the startup overhead.
# Work around https://bugs.launchpad.net/fuel/+bug/1467579.
sudo chmod +r /boot/vmlinuz*
eval "$(guestfish --listen)"
if (( ONESHOT )); then if (( ONESHOT )); then
rm -f "$IMG" rm -f "$IMG"
create_rootfs_img "$IMG" create_rootfs_img "$IMG"
sudo mount -o loop "$IMG" "$mnt" guestfish --remote \
download_rootfs "$ROOTFSVERSION" "$mnt" add "$IMG" label:img : \
launch : \
mount /dev/disk/guestfs/img /
download_rootfs "$ROOTFSVERSION" | tar_in /
else else
if (( ! SKIPIMG )); then if (( ! SKIPIMG )); then
rootfs_img="${ARCH_DIR}/${PROJECT_NAME}-vmtest-rootfs-${ROOTFSVERSION}.img" rootfs_img="${ARCH_DIR}/${PROJECT_NAME}-vmtest-rootfs-${ROOTFSVERSION}.img"
@@ -337,13 +357,15 @@ else
if [[ ! -e $rootfs_img ]]; then if [[ ! -e $rootfs_img ]]; then
tmp="$(mktemp "$rootfs_img.XXX.part")" tmp="$(mktemp "$rootfs_img.XXX.part")"
set_nocow "$tmp" set_nocow "$tmp"
truncate -s 2G "$tmp" truncate -s "$img_size" "$tmp"
mkfs.ext4 -q "$tmp" mkfs.ext4 -q "$tmp"
sudo mount -o loop "$tmp" "$mnt"
download_rootfs "$ROOTFSVERSION" "$mnt" # libguestfs supports hotplugging only with a libvirt
# backend, which we are not using here, so handle the
# temporary image in a separate session.
download_rootfs "$ROOTFSVERSION" |
guestfish -a "$tmp" tar-in - /
sudo umount "$mnt"
mv "$tmp" "$rootfs_img" mv "$tmp" "$rootfs_img"
tmp= tmp=
fi fi
@@ -351,11 +373,14 @@ else
rm -f "$IMG" rm -f "$IMG"
cp_img "$rootfs_img" "$IMG" cp_img "$rootfs_img" "$IMG"
fi fi
sudo mount -o loop "$IMG" "$mnt" guestfish --remote \
add "$IMG" label:img : \
launch : \
mount /dev/disk/guestfs/img /
fi fi
# Install vmlinux. # Install vmlinux.
vmlinux="$mnt/boot/vmlinux-${KERNELRELEASE}" vmlinux="/boot/vmlinux-${KERNELRELEASE}"
if [[ -v BUILDDIR || $ONESHOT -eq 0 ]]; then if [[ -v BUILDDIR || $ONESHOT -eq 0 ]]; then
if [[ -v BUILDDIR ]]; then if [[ -v BUILDDIR ]]; then
source_vmlinux="${BUILDDIR}/vmlinux" source_vmlinux="${BUILDDIR}/vmlinux"
@@ -368,15 +393,14 @@ if [[ -v BUILDDIR || $ONESHOT -eq 0 ]]; then
tmp= tmp=
fi fi
fi fi
echo "Copying vmlinux..." >&2
sudo rsync -cp --chmod 0644 "$source_vmlinux" "$vmlinux"
else else
# We could use "sudo zstd -o", but let's not run zstd as root with source_vmlinux="${ARCH_DIR}/vmlinux-${KERNELRELEASE}"
# input from the internet. download "${ARCH}/vmlinux-${KERNELRELEASE}.zst" | zstd -d >"$source_vmlinux"
download "${ARCH}/vmlinux-${KERNELRELEASE}.zst" |
zstd -d | sudo tee "$vmlinux" > /dev/null
sudo chmod 644 "$vmlinux"
fi fi
echo "Copying vmlinux..." >&2
guestfish --remote \
upload "$source_vmlinux" "$vmlinux" : \
chmod 644 "$vmlinux"
travis_fold end vmlinux_setup travis_fold end vmlinux_setup
@@ -384,7 +408,7 @@ REPO_PATH="${SELFTEST_REPO_PATH:-travis-ci/vmtest/bpf-next}"
LIBBPF_PATH="${REPO_ROOT}" \ LIBBPF_PATH="${REPO_ROOT}" \
VMTEST_ROOT="${VMTEST_ROOT}" \ VMTEST_ROOT="${VMTEST_ROOT}" \
REPO_PATH="${REPO_PATH}" \ REPO_PATH="${REPO_PATH}" \
VMLINUX_BTF=${vmlinux} ${VMTEST_ROOT}/build_selftests.sh VMLINUX_BTF=$(realpath ${source_vmlinux}) ${VMTEST_ROOT}/build_selftests.sh
travis_fold start bpftool_checks "Running bpftool checks..." travis_fold start bpftool_checks "Running bpftool checks..."
if [[ "${KERNEL}" = 'LATEST' ]]; then if [[ "${KERNEL}" = 'LATEST' ]]; then
@@ -402,23 +426,31 @@ if (( SKIPSOURCE )); then
else else
echo "Copying source files..." >&2 echo "Copying source files..." >&2
# Copy the source files in. # Copy the source files in.
sudo mkdir -p -m 0755 "$mnt/${PROJECT_NAME}" guestfish --remote \
mkdir-p "/${PROJECT_NAME}" : \
chmod 0755 "/${PROJECT_NAME}"
if [[ "${SOURCE_FULLCOPY}" == "1" ]]; then if [[ "${SOURCE_FULLCOPY}" == "1" ]]; then
git ls-files -z | sudo rsync --files-from=- -0cpt . "$mnt/${PROJECT_NAME}" git ls-files -z | tar --null --files-from=- -c | tar_in "/${PROJECT_NAME}"
else else
sudo mkdir -p -m 0755 ${mnt}/${PROJECT_NAME}/{selftests,travis-ci} guestfish --remote \
mkdir-p "/${PROJECT_NAME}/selftests" : \
chmod 0755 "/${PROJECT_NAME}/selftests" : \
mkdir-p "/${PROJECT_NAME}/travis-ci" : \
chmod 0755 "/${PROJECT_NAME}/travis-ci"
tree --du -shaC "${REPO_ROOT}/selftests/bpf" tree --du -shaC "${REPO_ROOT}/selftests/bpf"
sudo rsync -avm "${REPO_ROOT}/selftests/bpf" "$mnt/${PROJECT_NAME}/selftests/" tar -C "${REPO_ROOT}/selftests" -c bpf | tar_in "/${PROJECT_NAME}/selftests"
sudo rsync -avm "${REPO_ROOT}/travis-ci/vmtest" "$mnt/${PROJECT_NAME}/travis-ci/" tar -C "${REPO_ROOT}/travis-ci" -c vmtest | tar_in "/${PROJECT_NAME}/travis-ci"
fi fi
fi fi
setup_script="#!/bin/sh tmp=$(mktemp)
cat <<HERE >"$tmp"
"#!/bin/sh
echo 'Skipping setup commands' echo 'Skipping setup commands'
echo 0 > /exitstatus echo 0 > /exitstatus
chmod 644 /exitstatus" chmod 644 /exitstatus
HERE
# Create the init scripts. # Create the init scripts.
if [[ ! -z SETUPCMD ]]; then if [[ ! -z SETUPCMD ]]; then
@@ -427,30 +459,38 @@ if [[ ! -z SETUPCMD ]]; then
kernel="${KERNELRELEASE}" kernel="${KERNELRELEASE}"
if [[ -v BUILDDIR ]]; then kernel='latest'; fi if [[ -v BUILDDIR ]]; then kernel='latest'; fi
setup_envvars="export KERNEL=${kernel}" setup_envvars="export KERNEL=${kernel}"
setup_script=$(printf "#!/bin/sh cat <<HERE >"$tmp"
#!/bin/sh
set -eux set -eux
echo 'Running setup commands' echo 'Running setup commands'
%s ${setup_envvars}
set +e; %s; exitstatus=\$?; set -e set +e; ${setup_cmd}; exitstatus=\$?; set -e
echo \$exitstatus > /exitstatus echo \$exitstatus > /exitstatus
chmod 644 /exitstatus" "${setup_envvars}" "${setup_cmd}") chmod 644 /exitstatus
HERE
fi fi
echo "${setup_script}" | sudo tee "$mnt/etc/rcS.d/S50-run-tests" > /dev/null guestfish --remote \
sudo chmod 755 "$mnt/etc/rcS.d/S50-run-tests" upload "$tmp" /etc/rcS.d/S50-run-tests : \
chmod 755 /etc/rcS.d/S50-run-tests
fold_shutdown="$(travis_fold start shutdown)" fold_shutdown="$(travis_fold start shutdown)"
poweroff_script="#!/bin/sh cat <<HERE >"$tmp"
#!/bin/sh
echo ${fold_shutdown} echo ${fold_shutdown}
echo -e '\033[1;33mShutdown\033[0m\n' echo -e '\033[1;33mShutdown\033[0m\n'
poweroff" poweroff
echo "${poweroff_script}" | sudo tee "$mnt/etc/rcS.d/S99-poweroff" > /dev/null HERE
sudo chmod 755 "$mnt/etc/rcS.d/S99-poweroff" guestfish --remote \
upload "$tmp" /etc/rcS.d/S99-poweroff : \
chmod 755 /etc/rcS.d/S99-poweroff
rm "$tmp"
tmp=
sudo umount "$mnt" guestfish --remote exit
echo "Starting VM with $(nproc) CPUs..." echo "Starting VM with $(nproc) CPUs..."
@@ -480,14 +520,12 @@ esac
-drive file="$IMG",format=raw,index=1,media=disk,if=virtio,cache=none \ -drive file="$IMG",format=raw,index=1,media=disk,if=virtio,cache=none \
-kernel "$vmlinuz" -append "root=/dev/vda rw console=$console kernel.panic=-1 $APPEND" -kernel "$vmlinuz" -append "root=/dev/vda rw console=$console kernel.panic=-1 $APPEND"
sudo mount -o loop "$IMG" "$mnt" if exitstatus="$(guestfish --ro -a "$IMG" -i cat /exitstatus 2>/dev/null)"; then
if exitstatus="$(cat "$mnt/exitstatus" 2>/dev/null)"; then
printf '\nTests exit status: %s\n' "$exitstatus" >&2 printf '\nTests exit status: %s\n' "$exitstatus" >&2
else else
printf '\nCould not read tests exit status\n' >&2 printf '\nCould not read tests exit status\n' >&2
exitstatus=1 exitstatus=1
fi fi
sudo umount "$mnt"
travis_fold end shutdown travis_fold end shutdown