Encapsulate test environments within subdirectories

It is sometimes helpful to have multiple active test setups, and it is
also convenient to be able to destroy a test setup with one command
rather than having to delete multiple different files and directories.

This command adds a `-D` argument to setup.sh, allowing all activity in
the setup script to occur within a specified directory. When a
subdirectory is not specified, a default of `test.$(uname -r)` is used.

The run.sh script is also now aware of the test directory, searching for
a default of `test.$(uname -r)` and allowing manual specification by
setting the TESTDIR environment variable. If the TESTDIR does not exist,
run.sh reverts to running from the CWD.

Also, the image-creation script has been pulled out of a heredoc in
setup.sh into a standalone script, making maintenance a little easier.
This commit is contained in:
Andrew J. Hesford 2020-11-16 12:19:15 -05:00
parent 65a1a337d8
commit 095c328d42
6 changed files with 250 additions and 169 deletions

1
testing/.gitignore vendored
View File

@ -7,3 +7,4 @@ local.yaml
.config
dracut
dracut.conf.d
generate-zbm

View File

@ -10,11 +10,13 @@ The testing environment setup and runtime depends on the following tools:
# Creating a ZFSBootMenu Test Pool for QEMU
First, run `./setup.sh -a` to:
First, run `./setup.sh -a`; this will create, if necessary, a test directory
(chosen automatically or specified with the `-D` command-line flag) and, within
the test directory:
* Create a test pool
1. Create a 1GB RAW image file,
2. Attach it to the `loop0` loopback device,
1. Create a 2GB RAW image file,
2. Attach it to a loopback device,
3. Create a GPT label and a ZFS pool `ztest`,
4. Install Void base-minimal onto the pool,
5. Configure the installation, and

View File

@ -9,7 +9,7 @@ xbps-reconfigure -f glibc-locales
# Install a kernel and ZFS
xbps-install -S
xbps-install -y linux5.8 linux5.8-headers zfs
xbps-install -y linux5.9 linux5.9-headers zfs
# Setup ZFS in Dracut
cat << EOF > /etc/dracut.conf.d/zol.conf
@ -18,7 +18,7 @@ add_dracutmodules+=" zfs "
omit_dracutmodules+=" btrfs "
EOF
xbps-reconfigure -f linux5.8
xbps-reconfigure -f linux5.9
# Set kernel commandline
case "$(uname -m)" in

105
testing/image.sh Executable file
View File

@ -0,0 +1,105 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab
TESTDIR="${1?Usage: $0 <testdir>}"
if [ -z "${TESTDIR}" ] || [ ! -d "${TESTDIR}" ]; then
echo "ERROR: test directory must be specified and must exist"
exit 1
fi
XBPS_ARCH="$(uname -m)"
case "${XBPS_ARCH}" in
ppc64le)
URL="https://mirrors.servercentral.com/void-ppc/current"
;;
x86_64)
URL="https://mirrors.servercentral.com/voidlinux/current"
;;
*)
echo "ERROR: unsupported architecture"
exit 1
;;
esac
if [ -n "${MUSL}" ]; then
URL="${URL}/musl"
XBPS_ARCH="${XBPS_ARCH}-musl"
fi
export XBPS_ARCH
MNT="$( mktemp -d )" || exit 1
# shellcheck disable=SC2064
trap "rmdir '${MNT}'" EXIT
qemu-img create "${TESTDIR}/zfsbootmenu-pool.img" 2G
chown "$( stat -c %U . ):$( stat -c %G . )" "${TESTDIR}/zfsbootmenu-pool.img"
LOOP="$( losetup -f )" || exit 1
losetup "${LOOP}" "${TESTDIR}/zfsbootmenu-pool.img" || exit 1
# shellcheck disable=SC2064
trap "rmdir '${MNT}'; losetup -d '${LOOP}'" EXIT
kpartx -u "${LOOP}"
echo 'label: gpt' | sfdisk "${LOOP}"
zpool create -f \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-o autotrim=on \
-o cachefile=none \
-m none ztest "${LOOP}"
zfs snapshot -r ztest@barepool
zfs create -o mountpoint=none ztest/ROOT
zfs create -o mountpoint=/ -o canmount=noauto ztest/ROOT/void
zfs snapshot -r ztest@barebe
zfs set org.zfsbootmenu:commandline="spl_hostid=$( hostid ) ro quiet" ztest/ROOT
zpool set bootfs=ztest/ROOT/void ztest
zpool export ztest
zpool import -o cachefile=none -R "${MNT}" ztest || exit 1
# shellcheck disable=SC2064
trap "zpool export ztest; rmdir '${MNT}'; losetup -d '${LOOP}'" EXIT
zfs mount ztest/ROOT/void || exit 1
# shellcheck disable=SC2064
trap "umount -R '${MNT}'; zpool export ztest; rmdir '${MNT}'; losetup -d '${LOOP}'" EXIT
# https://github.com/project-trident/trident-installer/blob/master/src-sh/void-install-zfs.sh#L541
mkdir -p "${MNT}/var/db/xbps/keys"
cp /var/db/xbps/keys/*.plist "${MNT}/var/db/xbps/keys/."
mkdir -p "${MNT}/etc/xbps.d"
cp /etc/xbps.d/*.conf "${MNT}/etc/xbps.d/."
# /etc/runit/core-services/03-console-setup.sh depends on loadkeys from kbd
# /etc/runit/core-services/05-misc.sh depends on ip from iproute2
xbps-install -y -M -r "${MNT}" --repository="${URL}" \
base-minimal dracut ncurses-base kbd iproute2 dhclient openssh
cp /etc/hostid "${MNT}/etc/"
cp /etc/resolv.conf "${MNT}/etc/"
cp /etc/rc.conf "${MNT}/etc/"
mkdir -p "${MNT}/etc/xbps.d"
echo "repository=${URL}" > "${MNT}/etc/xbps.d/00-repository-main.conf"
mount -t proc proc "${MNT}/proc"
mount -t sysfs sys "${MNT}/sys"
mount -B /dev "${MNT}/dev"
mount -t devpts pts "${MNT}/dev/pts"
zfs snapshot -r ztest@pre-chroot
cp chroot.sh "${MNT}/root"
chroot "${MNT}" /root/chroot.sh

View File

@ -1,4 +1,5 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab
usage() {
cat <<EOF
@ -9,58 +10,17 @@ Usage: $0 [options]
-n Do not recreate the initramfs
-s Enable serial console on stdio
-v Set type of qemu display to use
-D Set test directory
EOF
}
# Support x86_64 and ppc64(le)
case "$(uname -m)" in
ppc64*)
BIN="qemu-system-ppc64"
KERNEL="vmlinux-bootmenu"
MACHINE="pseries,accel=kvm,kvm-type=HV,cap-hpt-max-page-size=4096"
APPEND="loglevel=7 timeout=5 root=zfsbootmenu:POOL=ztest"
SERDEV="hvc0"
;;
x86_64)
BIN="qemu-system-x86_64"
KERNEL="vmlinuz-bootmenu"
MACHINE="type=q35,accel=kvm"
APPEND="loglevel=7 timeout=5 root=zfsbootmenu:POOL=ztest"
SERDEV="ttyS0"
;;
esac
CMDOPTS="D:A:a:d:nsv:h"
DRIVE="-drive format=raw,file=zfsbootmenu-pool.img"
INITRD="initramfs-bootmenu.img"
MEMORY="2048M"
SMP="2"
CREATE=1
SERIAL=0
DISPLAY_TYPE=
# Override any default variables
#shellcheck disable=SC1091
[ -f .config ] && source .config
while getopts "A:a:d:nsv:h" opt; do
# First-pass option parsing just looks for test directory
while getopts "${CMDOPTS}" opt; do
case "${opt}" in
A)
AAPPEND+=( "$OPTARG" )
;;
a)
APPEND="${OPTARG}"
;;
d)
MDRIVE+=("-drive" "format=raw,file=${OPTARG}")
;;
n)
CREATE=0
;;
s)
SERIAL=1
;;
v)
DISPLAY_TYPE="${OPTARG}"
D)
TESTDIR="${OPTARG}"
;;
\?|h)
usage
@ -71,9 +31,78 @@ while getopts "A:a:d:nsv:h" opt; do
esac
done
if [ "${#MDRIVE[@]}" -gt 0 ]; then
DRIVE="${MDRIVE[*]}"
if [ -n "${TESTDIR}" ]; then
# If a test directory was specified, it must exist
if [ ! -d "${TESTDIR}" ]; then
echo "ERROR: test directory '${TESTDIR}' does not exist"
exit 1
fi
else
# If a test directory was not specified, try a default
TESTDIR="./test.$(uname -m)"
[ -d "${TESTDIR}" ] || TESTDIR="."
fi
# Support x86_64 and ppc64(le)
case "$(uname -m)" in
ppc64*)
BIN="qemu-system-ppc64"
KERNEL="${TESTDIR}/vmlinux-bootmenu"
MACHINE="pseries,accel=kvm,kvm-type=HV,cap-hpt-max-page-size=4096"
APPEND="loglevel=7 timeout=5 root=zfsbootmenu:POOL=ztest"
SERDEV="hvc0"
;;
x86_64)
BIN="qemu-system-x86_64"
KERNEL="${TESTDIR}/vmlinuz-bootmenu"
MACHINE="type=q35,accel=kvm"
APPEND="loglevel=7 timeout=5 root=zfsbootmenu:POOL=ztest"
SERDEV="ttyS0"
;;
esac
DRIVE=("-drive" "format=raw,file=${TESTDIR}/zfsbootmenu-pool.img")
INITRD="${TESTDIR}/initramfs-bootmenu.img"
MEMORY="2048M"
SMP="2"
CREATE=1
SERIAL=0
DISPLAY_TYPE=
# Override any default variables
#shellcheck disable=SC1091
[ -f .config ] && source .config
# Second-pass option parsing grabs all other options
OPTIND=1
while getopts "${CMDOPTS}" opt; do
case "${opt}" in
A)
AAPPEND+=( "$OPTARG" )
;;
a)
APPEND="${OPTARG}"
;;
d)
if ! _dimg="$(realpath -e "${TESTDIR}/${OPTARG}")"; then
echo "ERROR: disk image '${TESTDIR}/${OPTARG}' does not exist"
exit 1
fi
DRIVE+=("-drive" "format=raw,file=${_dimg}")
;;
n)
CREATE=0
;;
s)
SERIAL=1
;;
v)
DISPLAY_TYPE="${OPTARG}"
;;
*)
;;
esac
done
if [ -n "${DISPLAY_TYPE}" ]; then
# Use the indicated graphical display
@ -103,24 +132,27 @@ if ((CREATE)) ; then
[ -f "${KERNEL}" ] && rm "${KERNEL}"
[ -f "${INITRD}" ] && rm "${INITRD}"
# Try to find the local dracut first
PATH=./dracut:${PATH} ../bin/generate-zbm -c ./local.yaml
# Try to find the local dracut and generate-zbm first
if ! ( cd "${TESTDIR}" && PATH=./dracut:${PATH} ./generate-zbm -c ./local.yaml ); then
echo "ERROR: unable to create ZFSBootMenu images"
exit 1
fi
fi
# Ensure kernel and initramfs exist
if [ ! -f "${KERNEL}" ] ; then
echo "Missing kernel: ${KERNEL}"
exit
exit 1
elif [ ! -f "${INITRD}" ] ; then
echo "Missing initramfs: ${INITRD}"
exit
exit 1
fi
# shellcheck disable=SC2086
"${BIN}" \
-kernel "${KERNEL}" \
-initrd "${INITRD}" \
${DRIVE} \
"${DRIVE[@]}" \
-m "${MEMORY}" \
-smp "${SMP}" \
-cpu host \
@ -130,7 +162,7 @@ fi
"${DISPLAY_ARGS[@]}" \
-serial mon:stdio \
-netdev user,id=n1,hostfwd=tcp::2222-:22 -device virtio-net-pci,netdev=n1 \
-append "${APPEND}"
-append "${APPEND}" || exit 1
if ((SERIAL)); then
reset

View File

@ -1,5 +1,8 @@
#!/bin/bash
# vim: softtabstop=2 shiftwidth=2 expandtab
YAML=0
GENZBM=0
IMAGE=0
CONFD=0
DRACUT=0
@ -8,11 +11,13 @@ usage() {
cat <<EOF
Usage: $0 [options]
-y Create local.yaml
-g Create a generate-zbm symlink
-c Create dracut.conf.d
-d Create a local dracut tree for local mode
-i Create a test VM image
-a Perform all setup options
-m When making an image, use musl instead of glibc
-D Specify a test directory to use
EOF
}
@ -21,7 +26,7 @@ if [ $# -eq 0 ]; then
exit
fi
while getopts "ycdaim" opt; do
while getopts "ycgdaimD:" opt; do
case "${opt}" in
y)
YAML=1
@ -35,24 +40,44 @@ while getopts "ycdaim" opt; do
d)
DRACUT=1
;;
g)
GENZBM=1
;;
a)
YAML=1
CONFD=1
IMAGE=1
DRACUT=1
GENZBM=1
;;
m)
MUSL=1
;;
D)
TESTDIR="${OPTARG}"
;;
\?)
usage
exit
esac
done
if ((CONFD)) ; then
# Assign a default dest directory if one was not provided
if [ -z "${TESTDIR}" ]; then
TESTDIR="./test.$(uname -m)"
if ((MUSL)); then
TESTDIR="${TESTDIR}-musl"
fi
fi
TESTDIR="$(realpath "${TESTDIR}")" || exit 1
# Make sure the test directory exists
mkdir -p "${TESTDIR}" || exit 1
if ((CONFD)) && [ ! -d "${TESTDIR}/dracut.conf.d" ]; then
echo "Creating dracut.conf.d"
test -d "dracut.conf.d" || cp -Rp ../etc/zfsbootmenu/dracut.conf.d .
cp -Rp ../etc/zfsbootmenu/dracut.conf.d "${TESTDIR}"
fi
if ((DRACUT)) ; then
@ -67,122 +92,38 @@ if ((DRACUT)) ; then
exit 1
fi
if [ ! -d dracut ]; then
if [ ! -d "${TESTDIR}/dracut" ]; then
echo "Creating local dracut tree"
cp -a /usr/lib/dracut .
cp "${DRACUTBIN}" ./dracut
cp -a /usr/lib/dracut "${TESTDIR}"
cp "${DRACUTBIN}" "${TESTDIR}/dracut"
fi
# Make sure the zfsbootmenu module is a link to the repo version
test -d dracut/modules.d/90zfsbootmenu && rm -rf dracut/modules.d/90zfsbootmenu
test -L dracut/modules.d/90zfsbootmenu || ln -s ../../../90zfsbootmenu dracut/modules.d
_dracut_mods="${TESTDIR}/dracut/modules.d"
test -d "${_dracut_mods}" && rm -rf "${_dracut_mods}/90zfsbootmenu"
ln -s "$(realpath -e ../90zfsbootmenu)" "${_dracut_mods}"
fi
if ((GENZBM)) ; then
rm -f "${TESTDIR}/generate-zbm"
ln -s "$(realpath -e ../bin/generate-zbm)" "${TESTDIR}/generate-zbm"
fi
# Setup a local config file
if ((YAML)) ; then
echo "Configuring local.yaml"
cp ../etc/zfsbootmenu/config.yaml local.yaml
yq-go w -i local.yaml Components.ImageDir "$( pwd )"
yq-go w -i local.yaml Components.Versions false
yq-go w -i local.yaml Global.ManageImages true
yq-go w -i local.yaml Global.DracutConfDir "$( pwd )/dracut.conf.d"
yq-go w -i local.yaml Global.DracutFlags[+] -- "--local"
yq-go d -i local.yaml Global.BootMountPoint
yq-go r -P -C local.yaml
cp ../etc/zfsbootmenu/config.yaml "${TESTDIR}/local.yaml"
yq-go w -i "${TESTDIR}/local.yaml" Components.ImageDir "${TESTDIR}"
yq-go w -i "${TESTDIR}/local.yaml" Components.Versions false
yq-go w -i "${TESTDIR}/local.yaml" Global.ManageImages true
yq-go w -i "${TESTDIR}/local.yaml" Global.DracutConfDir "${TESTDIR}/dracut.conf.d"
yq-go w -i "${TESTDIR}/local.yaml" Global.DracutFlags[+] -- "--local"
yq-go d -i "${TESTDIR}/local.yaml" Global.BootMountPoint
yq-go r -P -C "${TESTDIR}/local.yaml"
fi
# Create an image
if ((IMAGE)) ; then
sudo env MUSL="${MUSL}" /bin/bash <<"EOF"
XBPS_ARCH="$(uname -m)"
case "${XBPS_ARCH}" in
ppc64le)
URL="https://mirrors.servercentral.com/void-ppc/current"
;;
x86_64)
URL="https://mirrors.servercentral.com/voidlinux/current"
;;
*)
echo "ERROR: unsupported architecture"
exit 1
;;
esac
if [ -n "${MUSL}" ]; then
URL="${URL}/musl"
XBPS_ARCH="${XBPS_ARCH}-musl"
fi
export XBPS_ARCH
MNT="$( mktemp -d )"
LOOP="$( losetup -f )"
qemu-img create zfsbootmenu-pool.img 2G
losetup "${LOOP}" zfsbootmenu-pool.img
kpartx -u "${LOOP}"
echo 'label: gpt' | sfdisk "${LOOP}"
zpool create -f \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-o autotrim=on \
-o cachefile=none \
-m none ztest "${LOOP}"
zfs snapshot -r ztest@barepool
zfs create -o mountpoint=none ztest/ROOT
zfs create -o mountpoint=/ -o canmount=noauto ztest/ROOT/void
zfs snapshot -r ztest@barebe
zfs set org.zfsbootmenu:commandline="spl_hostid=$( hostid ) ro quiet" ztest/ROOT
zpool set bootfs=ztest/ROOT/void ztest
zpool export ztest
zpool import -R "${MNT}" ztest
zfs mount ztest/ROOT/void
# https://github.com/project-trident/trident-installer/blob/master/src-sh/void-install-zfs.sh#L541
mkdir -p "${MNT}/var/db/xbps/keys"
cp /var/db/xbps/keys/*.plist "${MNT}/var/db/xbps/keys/."
mkdir -p "${MNT}/etc/xbps.d"
cp /etc/xbps.d/*.conf "${MNT}/etc/xbps.d/."
# /etc/runit/core-services/03-console-setup.sh depends on loadkeys from kbd
# /etc/runit/core-services/05-misc.sh depends on ip from iproute2
xbps-install -y -S -M -r "${MNT}" --repository="${URL}" \
base-minimal dracut ncurses-base kbd iproute2 dhclient openssh
cp /etc/hostid "${MNT}/etc/"
cp /etc/resolv.conf "${MNT}/etc/"
cp /etc/rc.conf "${MNT}/etc/"
mkdir -p "${MNT}/etc/xbps.d"
echo "repository=${URL}" > "${MNT}/etc/xbps.d/00-repository-main.conf"
mount -t proc proc "${MNT}/proc"
mount -t sysfs sys "${MNT}/sys"
mount -B /dev "${MNT}/dev"
mount -t devpts pts "${MNT}/dev/pts"
zfs snapshot -r ztest@pre-chroot
cp chroot.sh "${MNT}/root"
chroot "${MNT}" /root/chroot.sh
umount -R "${MNT}" && rmdir "${MNT}"
zpool export ztest
losetup -d "${LOOP}"
chown "$( stat -c %U . ):$( stat -c %G . )" zfsbootmenu-pool.img
EOF
sudo env MUSL="${MUSL}" ./image.sh "${TESTDIR}"
fi