Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/scripts/qemu-xfstests-bake-vm.sh
Original file line number Diff line number Diff line change
@@ -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}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually it should at least move under the openzfs org

I'm happy to create the repository under the openzfs organization so we have an official home for it. It looks like things are working well enough that it wouldn't be inconvenient. Just let me know when you're ready for this.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternately, OpenZFS could be built with ./configure --disable-pam which disables building the optional pam package. That said, it is nice to have this potential snag documented here for developers testing locally. Either way if fine with me, but I wanted to mention it.

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
58 changes: 58 additions & 0 deletions .github/workflows/scripts/qemu-xfstests-prepare.sh
Original file line number Diff line number Diff line change
@@ -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 '<details><summary>check.log (tail)</summary>'
echo
echo '```'
tail -n 40 "$RES/results/check.log"
echo '```'
echo '</details>'
fi
} >> "$SUMMARY" 2>/dev/null || true

exit 0
85 changes: 85 additions & 0 deletions .github/workflows/scripts/qemu-xfstests-run.sh
Original file line number Diff line number Diff line change
@@ -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 \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume compression=off and relatime=off are required for some tests as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general for now I was aiming for the config that passes the maximum amount of tests with minimal changes.
I had to do some archaeology to remember why I originally made these choices, but:

  • compression is off because there's a bunch of tests which explicitly don't want it (_require_no_compress), only implemented in btrfs until now. There might be a few more that aren't tagged as such by upstream that would still fail. The typical failure mode is trying to fill up the fs (either to ENOSPC,or to certain size), then count physical blocks or expect an error. Writing from urandom instead of a constant pattern would help, but that was a bigger change.
  • Good catch on relatime=off, it's not actually needed. There's a couple that rely on atime, but relatime is enough there.

testpool "${DEV}1"
sudo zfs create -o mountpoint=legacy -o recordsize=64K testpool/testfs
Comment thread
behlendorf marked this conversation as resolved.

sudo mkdir -p /mnt/test /mnt/scratch
sudo mount -t zfs testpool/testfs /mnt/test

cd "$HOME/xfstests"
cat > local.config <<EOF
export FSTYP=zfs
export TEST_DEV=testpool/testfs
export TEST_DIR=/mnt/test
export SCRATCH_MNT=/mnt/scratch
export SCRATCH_ZPOOL_NAME=scratchpool
export SCRATCH_DEV_POOL="${DEV}2 ${DEV}3 ${DEV}4"
EOF

# Apply the ZFS exclude list only for group runs; when a caller names tests
# explicitly they want exactly those (e.g. debugging an excluded failure).
EXCLUDE=""
case "$OPTS" in
*-g*) EXCLUDE="-E exclude.zfs.txt" ;;
esac

sudo dmesg -c > /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"
77 changes: 77 additions & 0 deletions .github/workflows/zfs-qemu-xfstests.yml
Original file line number Diff line number Diff line change
@@ -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
Loading