diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 03d7994..b1a0e2f 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -99,6 +99,7 @@ jobs: - name: Archive VM test results uses: actions/upload-artifact@v4 + if: always() with: name: vm-results-${{matrix.host_release}}-${{matrix.release}}-${{matrix.debootstrap}} if-no-files-found: error diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..2417bb5 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,23 @@ +# Tests + +The `tests` directory provides scripts and configuration files which are used within [grml-debootstrap's GitHub actions](https://github.com/grml/grml-debootstrap/actions). + +> [!CAUTION] +> executing the scripts is potentially dangerous and may destroy the host system and/or any data. Run the tests only on throw-away systems and at your own risk. + +The scripts are **not** designed to be executed manually, though it's possible to run them inside a local Debian throw-away VM. + +> [!NOTE] +> make sure to have at least 8GB disk space and 2GB memory available on your VM. + +Execute the following steps to build a Debian VM image (`qemu.img`) and run the tests against it: + +``` +sudo apt install git docker.io +git clone https://github.com/grml/grml-debootstrap +cd grml-debootstrap +sudo ./tests/docker-build-deb.sh --autobuild 01 +sudo ./tests/build-vm-and-test.sh setup +sudo ./tests/build-vm-and-test.sh run +sudo ./tests/build-vm-and-test.sh test +``` diff --git a/tests/build-vm-and-test.sh b/tests/build-vm-and-test.sh index 53076ac..c88c6c5 100755 --- a/tests/build-vm-and-test.sh +++ b/tests/build-vm-and-test.sh @@ -32,9 +32,11 @@ if [ ! -d ./tests ]; then fi if [ "$1" == "setup" ]; then - [ -x ./tests/goss ] || curl -fsSL https://goss.rocks/install | GOSS_DST="$(pwd)/tests" sh sudo apt-get update - sudo apt-get -qq -y install qemu-system-x86 kpartx python3-pexpect python3-serial + sudo apt-get -qq -y install curl qemu-system-x86 kpartx python3-pexpect python3-serial + # vncsnapshot might not be available, though we don't want to abort execution then + sudo apt-get -qq -y install vncsnapshot || true + [ -x ./tests/goss ] || curl -fsSL https://goss.rocks/install | GOSS_DST="$(pwd)/tests" sh # TODO: docker.io exit 0 fi diff --git a/tests/serial-console-connection b/tests/serial-console-connection index b44299a..30e6bb8 100755 --- a/tests/serial-console-connection +++ b/tests/serial-console-connection @@ -1,6 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import serial +import os +import shutil +import subprocess import sys import time from pexpect import fdpexpect @@ -15,7 +18,7 @@ parser.add_argument( help="serial console device to connect " + "to (e.g. /dev/pts/X)", ) parser.add_argument( - "--hostname", default="buster", help="hostname of the system for login process" + "--hostname", default="bookworm", help="hostname of the system for login process" ) parser.add_argument("--user", default="root", help="user name to use for login") parser.add_argument("--password", default="grml", help="password for login") @@ -25,6 +28,11 @@ parser.add_argument( type=int, help="Maximum time for finding the login prompt, in seconds", ) +parser.add_argument( + "--screenshot", + default="screenshot.jpg", + help="file name for screenshot captured via VNC on error", +) parser.add_argument( "--tries", default="12", @@ -74,6 +82,21 @@ def login(ser, hostname, user, password, timeout=5): child.expect("%s@%s" % (user, hostname), timeout=timeout) +def capture_vnc_screenshot(screenshot_file): + if not shutil.which("vncsnapshot"): + print("WARN: vncsnapshot not available, skipping vnc snapshot capturing.") + return + + print("Trying to capture screenshot via vncsnapshot to", screenshot_file) + + proc = subprocess.Popen(["vncsnapshot", "localhost", screenshot_file]) + proc.wait() + if proc.returncode != 0: + print("WARN: failed to capture vnc snapshot :(") + else: + print("Screenshot file '%s' available" % os.path.abspath(screenshot_file)) + + def main(): args = parser.parse_args() hostname = args.hostname @@ -81,6 +104,7 @@ def main(): port = args.port user = args.user commands = args.command + screenshot_file = args.screenshot ser = serial.Serial(port, 115200) ser.flushInput() @@ -116,6 +140,7 @@ def main(): # after poweroff, the serial device will probably vanish. do not attempt reading from it anymore. if not success: + capture_vnc_screenshot(screenshot_file) sys.exit(1) diff --git a/tests/test-vm.sh b/tests/test-vm.sh index 85e46c5..969709d 100755 --- a/tests/test-vm.sh +++ b/tests/test-vm.sh @@ -114,12 +114,14 @@ if [ "$success" = "0" ] ; then exit 1 fi +RC=0 "$TEST_PWD"/tests/serial-console-connection \ --tries 180 \ + --screenshot "$TEST_PWD/tests/screenshot.jpg" \ --port "$serial_port" \ --hostname "$VM_HOSTNAME" \ --poweroff \ - "mount -t 9p -o trans=virtio,version=9p2000.L,rw $MOUNT_TAG /mnt && cd /mnt && ./testrunner" + "mount -t 9p -o trans=virtio,version=9p2000.L,rw $MOUNT_TAG /mnt && cd /mnt && ./testrunner" || RC=$? if [ ! -d results ] || [ ! -f ./results/goss.tap ] || [ ! -f ./results/goss.exitcode ]; then echo "Running tests inside VM failed for unknown reason" >&2 @@ -133,7 +135,14 @@ fi echo "Finished serial console connection [timeout=${timeout}]." -mv results/* "$TESTS_RESULTSDIR/" +# in case of errors we might have captured a screenshot via VNC +if [ -r "${TEST_PWD}"/tests/screenshot.jpg ] ; then + cp "${TEST_PWD}"/tests/screenshot.jpg "${TESTS_RESULTSDIR}" +fi + +if [ -d results ] ; then + mv results/* "$TESTS_RESULTSDIR/" +fi bailout $RC