From 50f731def4e6ff764f01c055872d6ebdcee0dc82 Mon Sep 17 00:00:00 2001 From: Michal Domonkos Date: Thu, 12 Oct 2023 16:05:16 +0200 Subject: [PATCH] Standardize on OCI images in test-suite Instead of manually bootstrapping our own base "image" using a host specific script, just use the official, prebuilt OCI images with Podman/Docker. This has several advantages: - Standard, ubiquitous OCI images (easy support for other distros) - No manual setup of DNF, RPM macros, user namespaces and whatnot - Single recipe (Dockerfile) for both the local and CI purposes - Outsourced image caching (Podman storage) - Faster (just downloads the prebuilt image) - Less dependencies on the host Now that we've prepared mktree.podman for local use, just switch to it in cmake and remove the Fedora backend. Update the docs and comments accordingly, too, those should explain the details. Fixes: #2643 --- .gitignore | 1 - tests/CMakeLists.txt | 46 +++++----- tests/README.md | 38 +++++++-- tests/mktree.fedora | 195 ------------------------------------------- tests/mktree.podman | 12 ++- 5 files changed, 59 insertions(+), 233 deletions(-) delete mode 100755 tests/mktree.fedora diff --git a/.gitignore b/.gitignore index f327f80446..e6e33092d3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ /_build /docs/_site -/mktree.* diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7fd3510d06..1f6bd698d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -71,40 +71,32 @@ if (nproc GREATER 1) set(JOBS -j${nproc}) endif() -# Detect suitable mktree backend -find_program(BWRAP bwrap) -mark_as_advanced(BWRAP) -find_program(PODMAN NAMES podman docker) -mark_as_advanced(PODMAN) -if ("${MKTREE_BACKEND}" STREQUAL "") - if (BWRAP AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/mktree.${OS_NAME}) - set(MKTREE_BACKEND ${OS_NAME}) - elseif (PODMAN) - set(MKTREE_BACKEND podman) - else() - message(WARNING - "No suitable mktree backend found for this platform. " - "Disabling test-suite.") - return() - endif() -endif() -message(STATUS "Using mktree backend: ${MKTREE_BACKEND}") - # Set up mktree -if ("${MKTREE_BACKEND}" STREQUAL "podman") +set(MKTREE_BACKEND podman CACHE STRING "Mktree backend to use") +if (MKTREE_BACKEND STREQUAL "podman") find_program(PODMAN NAMES podman docker REQUIRED) - configure_file(Dockerfile Dockerfile COPYONLY) -else() - find_program(BWRAP bwrap REQUIRED) - if (PODMAN) - get_filename_component(PODMAN ${PODMAN} NAME) + find_program(BUILDAH buildah) + get_filename_component(PODMAN_NAME ${PODMAN} NAME) + mark_as_advanced(PODMAN PODMAN_NAME BUILDAH) + if (PODMAN_NAME STREQUAL "podman" AND BUILDAH AND + EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Dockerfile.${OS_NAME}) + set(MKTREE_NATIVE ON) + configure_file(Dockerfile.${OS_NAME} Dockerfile COPYONLY) add_custom_target(ci - COMMAND ./mktree.${PODMAN} build - COMMAND ./mktree.${PODMAN} check ${JOBS} $(TESTOPTS) + COMMAND ./mktree.podman build + COMMAND ./mktree.podman check --log ${JOBS} $(TESTOPTS) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + else() + set(MKTREE_NATIVE OFF) + configure_file(Dockerfile Dockerfile COPYONLY) endif() + set(MKTREE_MODE " (native: ${MKTREE_NATIVE})") +elseif(MKTREE_BACKEND STREQUAL "rootfs") + find_program(BWRAP bwrap REQUIRED) + mark_as_advanced(BWRAP) endif() +message(STATUS "Using mktree backend: ${MKTREE_BACKEND}${MKTREE_MODE}") configure_file(mktree.common mktree.common @ONLY) configure_file(mktree.${MKTREE_BACKEND} mktree @ONLY) diff --git a/tests/README.md b/tests/README.md index 96032ee0bb..3b08c4b20d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,25 @@ -To run these tests, you need at least these dependencies on the host: +To run these tests, you need either of: -1. [bwrap](https://github.com/containers/bubblewrap/) -1. [gdb](https://www.gnu.org/software/gdb/) -1. [gnupg](https://www.gnupg.org/) >= 2.0 +1. [podman](https://github.com/containers/podman) +1. [docker](https://github.com/docker) + +Optionally, the following is also recommended (see below): + +1. [buildah](https://github.com/containers/buildah) + +The test suite runs in a Podman/Docker container and supports two modes: + +1. *Native* - Exercises the local build of RPM against a minimal image of the + host Linux distribution. Currently, only Fedora Linux is supported. This + mode is optimized for local RPM development and requires Podman and Buildah. +1. *Non-native* - Performs a fresh build of RPM (including cmake configuration) + from the local checkout as part of a Fedora-based image. This mode is + optimized for portability (CI environment) and works with both Podman and + Docker. + +The mode is selected automatically by cmake based on the host distribution and +the container tools installed, with the native mode being preferred whenever +possible. Then run the command @@ -32,16 +49,21 @@ For all available options, see the output of the command: By default, tests are executed in parallel using all available cores, pass a specific -jN value to limit. -To drop into an Autotest-like shell, run: +To drop into an interactive Autotest-like shell, run: make atshell -See the printed help for details on how to use it. +This is like a singular, empty `RPMTEST_CHECK()` with a shell running in it and +a writable tree available at the path stored in `$RPMTEST`. From this shell, +you can run the same commands as a normal test would, such as `runroot rpm`. +This can be used to quickly prototype (or debug) a test. -You can also run a containerized shell with your RPM checkout: +You can also drop straight into the `$RPMTEST` container like so: make shell -To factory-reset the container, run: +This is just a shorthand for `make atshell` followed by `runroot_other bash`. + +To factory-reset the `$RPMTEST` container, run: make reset diff --git a/tests/mktree.fedora b/tests/mktree.fedora deleted file mode 100755 index 0e7d52b001..0000000000 --- a/tests/mktree.fedora +++ /dev/null @@ -1,195 +0,0 @@ -#!/bin/bash -# -# Fedora-native mktree backend using DNF to bootstrap a minimal OS tree with -# the RPM runtime and test dependencies but no stock RPM preinstalled. -# Lightweight snapshotting built into the test-suite is used for isolation. - -source ./mktree.common - -# Obtain source dir (or common git-worktree(1) if one exists) -git() { command git -C "@CMAKE_SOURCE_DIR@" "$@"; } -if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - SOURCE_DIR=$(git rev-parse --path-format=absolute --git-common-dir | - xargs dirname) -else - SOURCE_DIR=@CMAKE_SOURCE_DIR@ -fi - -# Build-agnostic dirs, prefer those in source dir -[ -d "$SOURCE_DIR/mktree.output" ] || SOURCE_DIR=$PWD -BASE_DIR=$SOURCE_DIR/mktree.output/base -CACHE_DIR=$SOURCE_DIR/mktree.cache - -# Build-specific dirs -INST_DIR=$PWD/mktree.output/inst -SANDBOX_DIR=$PWD/mktree.sandbox - -RPM_MACROS=" -%_netsharedpath %{nil} -%__transaction_systemd_inhibit %{nil} -" - -ATSHELL_MOTD=" -Welcome to RPM test environment! - -This is like an interactive test with a writable \$RPMTEST -tree, but with full host integration so you can use your -native tools to inspect or modify the tree. - -The shell runs in a user namespace WITHOUT filesystem -isolation so be mindful when using destructive commands. -Treat it like any other shell on your host. - -The usual test commands are available, e.g. - - runroot rpm ... - -DNF is configured to operate on the tree, use it simply -like this: - - dnf install ... - -To factory-reset the tree, leave this shell and run: - - make reset -" - -CMD=$1; shift - -dnf() -{ - HOME=$CACHE_DIR command dnf \ - --installroot=$BASE_DIR \ - --releasever=@OS_VERSION@ \ - --setopt=cachedir=$CACHE_DIR/dnf \ - --setopt=keepcache=1 \ - --setopt=install_weak_deps=0 \ - --disablerepo=\* --enablerepo=fedora,updates \ - --exclude=rpm,systemd-udev "$@" -} - -mount_tree() -{ - local dir=$SANDBOX_DIR - if [ "$1" == "--read-only" ]; then - unset dir - fi - source ./atlocal - RPMTREE=$INST_DIR:$BASE_DIR - RPMTEST=$SANDBOX_DIR/tree - snapshot mount $dir -} - -clean_up() -{ - [ -n "$RPMTEST" ] && snapshot umount - chmod -Rf u+rwX $BASE_DIR $SANDBOX_DIR rpmtests.dir -} - -# Run CMD in a user namespace (if not root already) -if [ $(id -u) != 0 ]; then - if [ -f /run/.toolboxenv ]; then - # toolbox(1) support - UNSHARE="sudo --preserve-env" - else - UNSHARE="unshare -r --mount --map-auto" - fi - $UNSHARE $0 $CMD "$@" - exit -fi - -trap clean_up EXIT - -case $CMD in - build) - mkdir -p $CACHE_DIR - echo "$RPM_MACROS" > $CACHE_DIR/.rpmmacros - - dnf install -y \ - bash \ - binutils \ - bubblewrap \ - bzip2 \ - cmake \ - coreutils \ - cpio \ - curl \ - dbus-libs \ - debugedit \ - diffutils \ - elfutils-libelf \ - elfutils-libs \ - file \ - file-libs \ - findutils \ - gawk \ - gcc \ - gdb-minimal \ - glibc \ - gpg \ - grep \ - gzip \ - ima-evm-utils \ - libacl \ - libarchive \ - libcap \ - libfsverity \ - libgomp \ - libzstd \ - lua-libs \ - make \ - openssl-libs \ - patch \ - pkgconf-pkg-config \ - popt \ - python3 \ - rpm-sequoia \ - sed \ - shadow-utils \ - sqlite-libs \ - tar \ - unzip \ - util-linux-core \ - which \ - xz \ - xz-libs \ - zlib \ - zstd - - # Enable DNS resolution - cp /etc/resolv.conf $BASE_DIR/etc/ - - # Point RPM to newly created database - echo "%_dbpath $(rpm --eval '%_dbpath')" > $BASE_DIR/root/.rpmmacros - - # Configure default shell for root user - cp -r $BASE_DIR/{etc/skel/.,/root} - - # Add RPM installation layer - rm -rf "$INST_DIR" - make_install $INST_DIR - ;; - atshell) - set -a - mount_tree - - export CACHE_DIR - export BASE_DIR=$RPMTEST - export -f dnf - - echo "$ATSHELL_MOTD" - $SHELL - ;; - shell) - mount_tree - snapshot shell "$@" - ;; - check) - mount_tree --read-only - snapshot exec --tmpfs /tmp --bind $PWD $PWD --setenv RPMTREE $RPMTREE \ - rpmtests -C $PWD "$@" - ;; - reset) - rm -rf "$SANDBOX_DIR" - ;; -esac diff --git a/tests/mktree.podman b/tests/mktree.podman index 1454b9b304..85d53bca15 100755 --- a/tests/mktree.podman +++ b/tests/mktree.podman @@ -1,7 +1,15 @@ #!/bin/bash # -# Podman-based mktree backend using an OCI image to build and run RPM. -# Works standalone (outside of a build directory) too. +# Podman-based mktree backend using OCI images. +# +# If a Dockerfile matching the host distribution is found, it is built and then +# combined with the local RPM build to produce the final image ("native" mode). +# Otherwise, the default Dockerfile is used and RPM is built as part of the +# image ("non-native" mode). +# +# This script can also be invoked directly from the source directory, in which +# case it performs a non-native build (useful for CI purposes). If invoked via +# the mktree.docker symlink, docker is used instead of podman. PROGRAM=$(basename $0) if [ "$PROGRAM" == "mktree" ]; then