From ee58cc28db97e7d0f3cadceba1dac9939b98aaca Mon Sep 17 00:00:00 2001 From: Bartosz Stebel Date: Mon, 15 Jun 2026 02:48:34 +0200 Subject: [PATCH] CI: add draft xfstests workflow Add a workflow that runs xfstests, as discussed in #15565. This reuses a good bit of the existing qemu steps and scripts, with some copypaste. It should be enough to get some rough results, once that works well we can integrate it properly. Signed-off-by: Bartosz Stebel --- .../scripts/qemu-xfstests-bake-vm.sh | 76 +++++++++++++++++ .../scripts/qemu-xfstests-prepare.sh | 58 +++++++++++++ .../workflows/scripts/qemu-xfstests-run.sh | 85 +++++++++++++++++++ .github/workflows/zfs-qemu-xfstests.yml | 77 +++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100755 .github/workflows/scripts/qemu-xfstests-bake-vm.sh create mode 100755 .github/workflows/scripts/qemu-xfstests-prepare.sh create mode 100755 .github/workflows/scripts/qemu-xfstests-run.sh create mode 100644 .github/workflows/zfs-qemu-xfstests.yml diff --git a/.github/workflows/scripts/qemu-xfstests-bake-vm.sh b/.github/workflows/scripts/qemu-xfstests-bake-vm.sh new file mode 100755 index 000000000000..177acc5bbfa6 --- /dev/null +++ b/.github/workflows/scripts/qemu-xfstests-bake-vm.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +###################################################################### +# Bake xfstests into the build VM image. +# +# Runs on the build machine (vm0) AFTER ZFS has been built and installed +# (qemu-4-build.sh, without --poweroff) +# Called on the runner as: +# ssh zfs@vm0 '$HOME/zfs/.github/workflows/scripts/qemu-xfstests-bake-vm.sh' $OS +# +# The script powers the VM off at the end (like qemu-4-build-vm.sh --poweroff) +###################################################################### + +set -eu + +OS="$1" + +# TODO: move the sources under openzfs +XFSTESTS_REPO="${XFSTESTS_REPO:-https://github.com/implr/xfstests}" +XFSTESTS_BRANCH="${XFSTESTS_BRANCH:-zfs}" + +echo "##[group]Install xfstests dependencies" +case "$OS" in + debian*|ubuntu*) + sudo apt-get update + # Build toolchain + xfstests build/runtime deps + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git build-essential autoconf automake libtool pkg-config gettext \ + uuid-dev libattr1-dev libacl1-dev libaio-dev libgdbm-dev libssl-dev \ + xfsprogs e2fsprogs attr acl quota gdisk parted + # optional extras for a couple tests + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y fio dbench || true + ;; + *) + echo "xfstests bake is only implemented for debian/ubuntu so far" >&2 + exit 1 + ;; +esac +echo "##[endgroup]" + +echo "##[group]Create xfstests service users" +# xfstests insists on these accounts. Digit-leading name +# needs --badnames on modern shadow-utils. +sudo groupadd -f fsgqa +for u in fsgqa fsgqa2 123456-fsgqa; do + if ! id "$u" &>/dev/null; then + sudo useradd --badnames -g fsgqa -m "$u" 2>/dev/null \ + || sudo useradd -g fsgqa -m "$u" 2>/dev/null || true + fi +done + +# xfstests runs as root and drops to fsgqa via `su` (common/rc _user_do/_su). +# Installing OpenZFS pulls in libpam-zfs, which registers pam_zfs_key.so in the +# PAM stack to unlock users' encrypted home datasets at login. Its *session* +# hook prompts "Password:" when root su's to fsgqa (to derive the key for the +# nonexistent rpool/home/fsgqa), and that prompt lands in test output and fails +# every _user_do-based test (generic/123, 128, 314, ...). We don't use encrypted +# homes here, so strip pam_zfs_key from the PAM config. +sudo pam-auth-update --package --remove zfs_key 2>/dev/null || true +sudo sed -i '/pam_zfs_key/d' \ + /etc/pam.d/common-auth /etc/pam.d/common-session \ + /etc/pam.d/common-password /etc/pam.d/common-account 2>/dev/null || true +echo "##[endgroup]" + +echo "##[group]Clone + build xfstests ($XFSTESTS_BRANCH)" +rm -rf "$HOME/xfstests" +git clone --depth 1 -b "$XFSTESTS_BRANCH" "$XFSTESTS_REPO" "$HOME/xfstests" +cd "$HOME/xfstests" +make -j"$(nproc)" +echo "xfstests baked at $HOME/xfstests ($(git rev-parse --short HEAD))" +echo "##[endgroup]" + +# reset cloud-init and power off +sudo cloud-init clean --logs +sync && sleep 2 && sudo poweroff & +exit 0 diff --git a/.github/workflows/scripts/qemu-xfstests-prepare.sh b/.github/workflows/scripts/qemu-xfstests-prepare.sh new file mode 100755 index 000000000000..33d5993c121a --- /dev/null +++ b/.github/workflows/scripts/qemu-xfstests-prepare.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +###################################################################### +# Collect xfstests results off the test VM, tar them for upload, and +# write a short GitHub job summary. Runs on the runner; always() in the +# workflow so we still grab logs (and the console) on a guest crash. +###################################################################### + +set -eu + +source /var/tmp/env.txt +RES="$RESPATH" # /var/tmp/test_results +mkdir -p "$RES" + +# Pull results + logs off vm1 (best-effort; the VM may have crashed). +rsync -arL zfs@vm1:xfstests/results "$RES/" 2>/dev/null || true +scp zfs@vm1:xfstests/local.config "$RES/" 2>/dev/null || true +scp 'zfs@vm1:/var/tmp/dmesg-*.txt' "$RES/" 2>/dev/null || true +scp 'zfs@vm1:/var/tmp/tests-exitcode.txt' "$RES/" 2>/dev/null || true +cp -f /var/tmp/xfstests-run.log "$RES/" 2>/dev/null || true +# qemu-5-setup.sh already streams the VM serial console to $RES/vm1/console.txt. + +TARNAME="xfstests-$OS" +TAR="/tmp/$TARNAME.tar.bz2" +mv "$RES" "$(dirname "$RES")/$TARNAME" +tar cjf "$TAR" -C "$(dirname "$RES")" -h "$TARNAME" || true +mv "$(dirname "$RES")/$TARNAME" "$RES" + +# --- Job summary ---------------------------------------------------- +SUMMARY="${GITHUB_STEP_SUMMARY:-/dev/stdout}" +{ + echo "## xfstests on ${OSNAME:-$OS}" + echo + if [ -f "$RES/tests-exitcode.txt" ]; then + rv=$(cat "$RES/tests-exitcode.txt") + if [ "$rv" = "0" ]; then + echo ":thumbsup: \`./check\` exited 0 — all selected tests passed." + else + echo ":warning: \`./check\` exited $rv — failures (or a notrun treated as error)." + fi + else + echo ":interrobang: no exit code recorded — the VM may have crashed mid-run." + fi + echo + + # xfstests prints a "Failures:" / "Passed all N tests" line near the end of + # results/check.log. Surface the tail. + if [ -f "$RES/results/check.log" ]; then + echo '
check.log (tail)' + echo + echo '```' + tail -n 40 "$RES/results/check.log" + echo '```' + echo '
' + fi +} >> "$SUMMARY" 2>/dev/null || true + +exit 0 diff --git a/.github/workflows/scripts/qemu-xfstests-run.sh b/.github/workflows/scripts/qemu-xfstests-run.sh new file mode 100755 index 000000000000..ca423ace75b9 --- /dev/null +++ b/.github/workflows/scripts/qemu-xfstests-run.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +###################################################################### +# Run xfstests +# +# - called with 'guest' -> runs inside the test VM (vm1) +# called on runner: qemu-xfstests-run.sh +# called on qemu-vm: qemu-xfstests-run.sh guest +# +###################################################################### + +set -eu + +############################ +# Runner side (no args) +############################ +if [ -z "${1:-}" ]; then + source /var/tmp/env.txt + SCRIPT='$HOME/zfs/.github/workflows/scripts/qemu-xfstests-run.sh' + # Pass the user ./check arguments through to the guest. + ssh zfs@vm1 "OS='$OS' XFSTESTS_OPTIONS='${XFSTESTS_OPTIONS:-}' $SCRIPT guest" \ + 2>&1 | stdbuf -oL tee /var/tmp/xfstests-run.log + exit "${PIPESTATUS[0]}" +fi + +############################ +# VM side +############################ +OS="${OS:?}" +OPTS="${XFSTESTS_OPTIONS:--g quick}" +export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" + +sudo -E modprobe zfs + +# Longer RCU timeouts (as in qemu-6-tests.sh) +rcu="/sys/module/rcupdate/parameters/rcu_cpu_stall_timeout" +test -f "$rcu" && echo 120 | sudo tee "$rcu" >/dev/null || true + +# Carve the 64 GiB tests disk (attached by qemu-5-setup.sh as the 2nd virtio +# disk) into a TEST_DEV partition + three SCRATCH pool members. +DEV=/dev/vdb +sudo wipefs -a "$DEV" || true +sudo sgdisk -Z "$DEV" +sudo sgdisk -n1:0:+20G -n2:0:+14G -n3:0:+14G -n4:0:0 "$DEV" +sudo partprobe "$DEV" +sleep 2 + +# Persistent test pool/dataset. +# (acltype=posix + xattr=sa are required for several generic/ tests). +sudo zpool create -f \ + -O mountpoint=legacy -O acltype=posix -O xattr=sa \ + -O compression=off -O relatime=off \ + testpool "${DEV}1" +sudo zfs create -o mountpoint=legacy -o recordsize=64K testpool/testfs + +sudo mkdir -p /mnt/test /mnt/scratch +sudo mount -t zfs testpool/testfs /mnt/test + +cd "$HOME/xfstests" +cat > local.config < /var/tmp/dmesg-prerun.txt || true + +RV=0 +# shellcheck disable=SC2086 # word-splitting of OPTS/EXCLUDE is intentional +sudo HOST_OPTIONS="$PWD/local.config" ./check $EXCLUDE $OPTS || RV=$? +echo "$RV" | sudo tee /var/tmp/tests-exitcode.txt >/dev/null + +sudo dmesg > /var/tmp/dmesg-postrun.txt || true +sync +exit "$RV" diff --git a/.github/workflows/zfs-qemu-xfstests.yml b/.github/workflows/zfs-qemu-xfstests.yml new file mode 100644 index 000000000000..4f7196df75ea --- /dev/null +++ b/.github/workflows/zfs-qemu-xfstests.yml @@ -0,0 +1,77 @@ +name: zfs-qemu-xfstests + +on: + workflow_dispatch: + inputs: + os: + type: string + required: false + default: "ubuntu24" + description: "Distribution to test on (e.g. ubuntu24, debian13)" + xfstests_options: + type: string + required: false + default: "-g quick" + description: "Arguments for xfstests' ./check (e.g. '-g quick', '-g auto', 'generic/467')" + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + xfstests: + name: xfstests-${{ inputs.os }} + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup QEMU + timeout-minutes: 60 + run: .github/workflows/scripts/qemu-1-setup.sh + + - name: Start build machine + timeout-minutes: 10 + run: | + .github/workflows/scripts/qemu-2-start.sh ${{ inputs.os }} + # force single vm for now + sed -i 's/^VMs=.*/VMs="1"/' /var/tmp/env.txt + + - name: Install dependencies + timeout-minutes: 60 + run: .github/workflows/scripts/qemu-3-deps.sh --poweroff ${{ inputs.os }} + + # NOTE: no --poweroff here, unlike zfs-qemu.yml — we keep vm0 up so the + # bake step can install xfstests before the image is snapshotted. + - name: Build modules + timeout-minutes: 30 + run: .github/workflows/scripts/qemu-4-build.sh --enable-debug ${{ inputs.os }} + + - name: Bake xfstests into image + timeout-minutes: 30 + run: ssh zfs@vm0 '$HOME/zfs/.github/workflows/scripts/qemu-xfstests-bake-vm.sh' ${{ inputs.os }} + + - name: Setup testing machines + timeout-minutes: 5 + run: .github/workflows/scripts/qemu-5-setup.sh + + - name: Run xfstests + timeout-minutes: 270 + run: .github/workflows/scripts/qemu-xfstests-run.sh + env: + XFSTESTS_OPTIONS: ${{ inputs.xfstests_options }} + + - name: Prepare artifacts + if: always() + timeout-minutes: 10 + run: .github/workflows/scripts/qemu-xfstests-prepare.sh + + - uses: actions/upload-artifact@v7 + id: artifact-upload + if: always() + with: + name: xfstests-${{ inputs.os }} + path: /tmp/xfstests-${{ inputs.os }}.tar.bz2 + archive: false + if-no-files-found: ignore