diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index e22fec46..3b3ec6fa 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -102,3 +102,43 @@ jobs: name: vm-results-${{matrix.host_release}}-${{matrix.release}}-${{matrix.debootstrap}} if-no-files-found: error path: tests/results/ + + b2b-diff: + needs: build-debian + # We want a working shell, qemu, python and docker. Specific version should not matter (much). + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Download built deb + uses: actions/download-artifact@v3 + with: + name: deb-unstable + + - run: ./tests/gha-b2b-diff.sh run + name: "Build VM image twice" + env: + HOST_RELEASE: unstable + RELEASE: trixie + DEBOOTSTRAP: debootstrap + + - run: ./tests/gha-b2b-diff.sh test + name: "Diff built images" + env: + RELEASE: unstable + + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 45 + with: + limit-access-to-actor: true + + - name: Archive VM test results + uses: actions/upload-artifact@v3 + with: + name: b2b-diff + if-no-files-found: error + path: diff.txt + diff --git a/tests/b2b-known-differences b/tests/b2b-known-differences new file mode 100644 index 00000000..e0759fb1 --- /dev/null +++ b/tests/b2b-known-differences @@ -0,0 +1,36 @@ +# installation log files +/var/log/* +# device files cannot be compared by diff +/dev/console +/dev/full +/dev/null +/dev/ptmx +/dev/random +/dev/tty +/dev/urandom +/dev/zero +# postfix - unknown reason for difference +/etc/aliases.db +# ssh keys are generated randomly +/etc/ssh/ssh_host_*key* +# ssl certificates generated by ssl-cert +/etc/ssl/certs/ssl-cert-snakeoil.pem +/etc/ssl/private/ssl-cert-snakeoil.key +/etc/ssl/certs/*.0 +# kernel initrd images are generated during install and embed timestamp? +/boot/initrd.img-* +# generated by dbus during install. TODO: delete? +/var/lib/dbus/machine-id +/etc/machine-id +# root password hash is randomized +/etc/shadow +# embed creation timestamp +/etc/mdadm/mdadm.conf +/etc/lvm/backup/vg0 +# embed root partition UUID +/etc/fstab +/boot/grub/grub.cfg +# bug? +/etc/debootstrap/config +# TBD: why? +/var/cache/ldconfig/aux-cache diff --git a/tests/docker-test-b2b.sh b/tests/docker-test-b2b.sh new file mode 100755 index 00000000..ade48271 --- /dev/null +++ b/tests/docker-test-b2b.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Diff two built VM images for unaccounted differences. + +set -eu -o pipefail + +if [ "$#" -ne 2 ]; then + echo "$0: Invalid arguments" >&2 + echo "Expect: $0 IMG1 IMG2" >&2 + exit 1 +fi +IMG1="$1" +IMG2="$2" + +set -x + +MNTDIR1=$(mktemp -d) +MNTDIR2=$(mktemp -d) + +# Assumes root partition is at 4MB offset. +mount -oloop,offset=4194304 "$IMG1" "$MNTDIR1" +mount -oloop,offset=4194304 "$IMG2" "$MNTDIR2" + +set +x + +while read -r pattern; do + if [ -n "$pattern" ]; then + echo "Removing known difference before diffing: $pattern" + rm -rfv "$MNTDIR1"$pattern "$MNTDIR2"$pattern + fi +done < <(grep -v '^#' ./tests/b2b-known-differences) + +set -x +exec diff -Nru \ + --no-dereference \ + "$MNTDIR1" "$MNTDIR2" | tee diff.txt diff --git a/tests/gha-b2b-diff.sh b/tests/gha-b2b-diff.sh new file mode 100755 index 00000000..18a6d749 --- /dev/null +++ b/tests/gha-b2b-diff.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Install an already built grml-debootstrap.deb in docker and use it to +# build two test VM images, with the same configuration. Then diff the +# two images for unaccounted differences. + +set -eu -o pipefail + +usage() { + echo "Usage: $0 run" + echo " then: $0 test" + echo "WARNING: $0 is potentially dangerous and may destroy the host system and/or any data." + exit 0 +} + +if [ "${1:-}" == "--help" ] || [ "${1:-}" == "help" ]; then + usage +fi + +if [ -z "${1:-}" ]; then + echo "$0: unknown parameters, see --help" >&2 + exit 1 +fi + +set -x + +if [ ! -d ./tests ]; then + echo "$0: Started from incorrect working directory" >&2 + exit 1 +fi + +# Debian version to install using grml-debootstrap +RELEASE="${RELEASE:-bookworm}" +HOST_RELEASE="${HOST_RELEASE:-unstable}" + +# debootstrap to use, default empty (let grml-debootstrap decide) +DEBOOTSTRAP="${DEBOOTSTRAP:-}" + +build_image() { + # we need to run in privileged mode to be able to use loop devices + docker run --privileged --rm -i \ + -v "$(pwd)":/code \ + -e TERM="$TERM" \ + -e DEBOOTSTRAP="$DEBOOTSTRAP" \ + -w /code \ + debian:"$HOST_RELEASE" \ + bash -c './tests/docker-install-deb.sh '"$DEB_NAME"' && ./tests/docker-build-vm.sh '"$(id -u)"' '"/code/$1"' '"$RELEASE" +} + +if [ "$1" == "run" ]; then + # Debian version on which grml-debootstrap will *run* + HOST_RELEASE="${HOST_RELEASE:-bookworm}" + + DEB_NAME=$(ls ./grml-debootstrap*.deb || true) + if [ -z "$DEB_NAME" ]; then + echo "$0: No grml-debootstrap*.deb found, aborting" >&2 + exit 1 + fi + + build_image qemu-1.img + build_image qemu-2.img + +elif [ "$1" == "test" ]; then + exec docker run --privileged --rm -i \ + -v "$(pwd)":/code \ + -e TERM="$TERM" \ + -e DEBOOTSTRAP="$DEBOOTSTRAP" \ + -w /code \ + debian:"$HOST_RELEASE" \ + ./tests/docker-test-b2b.sh qemu-1.img qemu-2.img + +else + echo "$0: unknown parameters, see --help" >&2 + exit 1 +fi + +# EOF