From cbc9082fac68278c1fe24a0b0a97c1d8d8f72862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Tue, 1 Dec 2020 16:20:13 +0100 Subject: [PATCH] tests: move the libvirt test logic out of Jenkinsfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All tests in /usr/libexec/tests/osbuild-composer should be able to run without any arguments. This was not a case of libvirt.sh - it required two arguments set by some Jenkinsfile logic. This commit moves test/cases/libvirt.sh to tools/libvirt_test.sh and extracts the logic controlling the test case from Jenkinsfile to test/cases/libvirt.sh. Signed-off-by: OndΕ™ej Budai --- osbuild-composer.spec | 1 + schutzbot/Jenkinsfile | 64 +++----- test/cases/libvirt.sh | 339 +----------------------------------------- tools/libvirt_test.sh | 338 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 373 deletions(-) mode change 100755 => 100644 test/cases/libvirt.sh create mode 100755 tools/libvirt_test.sh diff --git a/osbuild-composer.spec b/osbuild-composer.spec index 22dbc1765..e6dfa53c8 100644 --- a/osbuild-composer.spec +++ b/osbuild-composer.spec @@ -174,6 +174,7 @@ install -m 0755 -vp tools/provision.sh %{buildroot}%{_l install -m 0755 -vp tools/image-info %{buildroot}%{_libexecdir}/osbuild-composer-test/ install -m 0755 -vp tools/run-koji-container.sh %{buildroot}%{_libexecdir}/osbuild-composer-test/ install -m 0755 -vp tools/koji-compose.py %{buildroot}%{_libexecdir}/osbuild-composer-test/ +install -m 0755 -vp tools/libvirt_test.sh %{buildroot}%{_libexecdir}/osbuild-composer-test/ install -m 0755 -vd %{buildroot}%{_libexecdir}/tests/osbuild-composer install -m 0755 -vp test/cases/* %{buildroot}%{_libexecdir}/tests/osbuild-composer/ diff --git a/schutzbot/Jenkinsfile b/schutzbot/Jenkinsfile index b9e777c92..6ab850079 100644 --- a/schutzbot/Jenkinsfile +++ b/schutzbot/Jenkinsfile @@ -118,7 +118,7 @@ pipeline { agent { label "f32cloudbase && x86_64 && aws" } environment { TEST_TYPE = "base" } steps { - run_tests('base', 'bios') + run_tests('base') } post { always { @@ -138,7 +138,7 @@ pipeline { DISTRO_CODE = "fedora32" } steps { - run_tests('image', 'bios') + run_tests('image') } post { always { @@ -159,7 +159,7 @@ pipeline { AWS_API_TEST_SHARE_ACCOUNT = credentials('aws-credentials-share-account') } steps { - run_tests('integration', 'bios') + run_tests('integration') } post { always { @@ -170,7 +170,7 @@ pipeline { stage('F32 OSTree') { agent { label "f32cloudbase && psi && x86_64" } steps { - run_tests('ostree', 'bios') + run_tests('ostree') } post { always { @@ -193,7 +193,7 @@ pipeline { agent { label "f33cloudbase && x86_64 && aws" } environment { TEST_TYPE = "base" } steps { - run_tests('base', 'bios') + run_tests('base') } post { always { @@ -213,7 +213,7 @@ pipeline { DISTRO_CODE = "fedora33" } steps { - run_tests('image', 'bios') + run_tests('image') } post { always { @@ -234,7 +234,7 @@ pipeline { AWS_API_TEST_SHARE_ACCOUNT = credentials('aws-credentials-share-account') } steps { - run_tests('integration', 'bios') + run_tests('integration') } post { always { @@ -245,7 +245,7 @@ pipeline { stage('F33 OSTree') { agent { label "f33cloudbase && psi && x86_64" } steps { - run_tests('ostree', 'bios') + run_tests('ostree') } post { always { @@ -257,7 +257,7 @@ pipeline { agent { label "f33cloudbase && aarch64 && aws" } environment { TEST_TYPE = "base" } steps { - run_tests('base', 'bios') + run_tests('base') } post { always { @@ -276,7 +276,7 @@ pipeline { DISTRO_CODE = "fedora33" } steps { - run_tests('image', 'bios') + run_tests('image') } post { always { @@ -295,7 +295,7 @@ pipeline { RHN_REGISTRATION_SCRIPT = credentials('rhn-register-script-production') } steps { - run_tests('base', 'bios') + run_tests('base') } post { always { @@ -316,7 +316,7 @@ pipeline { DISTRO_CODE = "rhel8" } steps { - run_tests('image', 'bios') + run_tests('image') } post { always { @@ -338,7 +338,7 @@ pipeline { AWS_API_TEST_SHARE_ACCOUNT = credentials('aws-credentials-share-account') } steps { - run_tests('integration', 'bios') + run_tests('integration') } post { always { @@ -349,7 +349,7 @@ pipeline { stage('EL8 OSTree') { agent { label "rhel8cloudbase && psi && x86_64" } steps { - run_tests('ostree', 'bios') + run_tests('ostree') } post { always { @@ -374,7 +374,7 @@ pipeline { TEST_TYPE = "base" } steps { - run_tests('base', 'hybrid') + run_tests('base') } post { always { @@ -394,7 +394,7 @@ pipeline { DISTRO_CODE = "rhel84" } steps { - run_tests('image', 'hybrid') + run_tests('image') } post { always { @@ -415,7 +415,7 @@ pipeline { AWS_IMAGE_TEST_CREDS = credentials('aws-credentials-osbuild-image-test') } steps { - run_tests('integration', 'hybrid') + run_tests('integration') } post { always { @@ -426,7 +426,7 @@ pipeline { stage('EL8.4 OSTree') { agent { label "rhel84cloudbase && psi && x86_64" } steps { - run_tests('ostree', 'hybrid') + run_tests('ostree') } post { always { @@ -463,7 +463,7 @@ pipeline { // Set up a function to hold the steps needed to run the tests so we don't // need to copy/paste the same lines over and over above. -void run_tests(test_type, boot_type) { +void run_tests(test_type) { // Get CI machine details. sh ( @@ -506,30 +506,10 @@ void run_tests(test_type, boot_type) { script: "/usr/libexec/tests/osbuild-composer/koji.sh" ) - // Run the qcow2 BIOS boot test. + // Run the libvirt test. sh ( - label: "Integration test: QCOW2, BIOS boot", - script: "/usr/libexec/tests/osbuild-composer/libvirt.sh qcow2" - ) - - if (boot_type == 'hybrid') { - // Run the qcow2 UEFI boot test. - sh ( - label: "Integration test: QCOW2, UEFI boot", - script: "/usr/libexec/tests/osbuild-composer/libvirt.sh qcow2 uefi" - ) - } - - // Run the openstack test. - sh ( - label: "Integration test: OpenStack", - script: "/usr/libexec/tests/osbuild-composer/libvirt.sh openstack" - ) - - // Run the VHD/Azure test. - sh ( - label: "Integration test: VHD", - script: "/usr/libexec/tests/osbuild-composer/libvirt.sh vhd" + label: "Integration test: libvirt", + script: "/usr/libexec/tests/osbuild-composer/libvirt.sh" ) // Run the AWS test. diff --git a/test/cases/libvirt.sh b/test/cases/libvirt.sh old mode 100755 new mode 100644 index 5f9ecc7f1..080397c06 --- a/test/cases/libvirt.sh +++ b/test/cases/libvirt.sh @@ -1,341 +1,18 @@ #!/bin/bash set -euo pipefail -OSBUILD_COMPOSER_TEST_DATA=/usr/share/tests/osbuild-composer/ - # Get OS data. source /etc/os-release -ARCH=$(uname -m) -# Provision the software under tet. +# Provision the software under test. /usr/libexec/osbuild-composer-test/provision.sh -# Take the image type passed to the script or use qcow2 by default if nothing -# was passed. -IMAGE_TYPE=${1:-qcow2} -# Take the boot type passed to the script or use BIOS by default if nothing -# was passed. -BOOT_TYPE=${2:-bios} +# Test the images +/usr/libexec/osbuild-composer-test/libvirt_test.sh qcow2 +/usr/libexec/osbuild-composer-test/libvirt_test.sh openstack +/usr/libexec/osbuild-composer-test/libvirt_test.sh vhd -# Select the file extension based on the image that we are building. -IMAGE_EXTENSION=$IMAGE_TYPE -if [[ $IMAGE_TYPE == 'openstack' ]]; then - IMAGE_EXTENSION=qcow2 +# RHEL 8.4 images also supports uefi, check that +if [[ "${ID}-${VERSION_ID}" == "rhel-8.4" ]]; then + /usr/libexec/osbuild-composer-test/libvirt_test.sh qcow2 uefi fi - -# RHEL 8 cannot boot a VMDK using libvirt. See BZ 999789. -if [[ $IMAGE_TYPE == vmdk ]]; then - echo "🀷 libvirt cannot boot stream-optimized VMDK." - exit 0 -fi - -# Apply lorax patch to work around pytoml issues in RHEL 8.x. -# See BZ 1843704 or https://github.com/weldr/lorax/pull/1030 for more details. -if [[ $ID == rhel ]]; then - sudo sed -r -i 's#toml.load\(args\[3\]\)#toml.load(open(args[3]))#' \ - /usr/lib/python3.6/site-packages/composer/cli/compose.py - sudo rm -f /usr/lib/python3.6/site-packages/composer/cli/compose.pyc -fi - -# Colorful output. -function greenprint { - echo -e "\033[1;32m${1}\033[0m" -} - -# Start libvirtd and test it. -greenprint "πŸš€ Starting libvirt daemon" -sudo systemctl start libvirtd -sudo virsh list --all > /dev/null - -# Set a customized dnsmasq configuration for libvirt so we always get the -# same address on bootup. -sudo tee /tmp/integration.xml > /dev/null << EOF - - integration - 1c8fe98c-b53a-4ca4-bbdb-deb0f26b3579 - - - - - - - - - - - - - - -EOF -if ! sudo virsh net-info integration > /dev/null 2>&1; then - sudo virsh net-define /tmp/integration.xml - sudo virsh net-start integration -fi - -# Allow anyone in the wheel group to talk to libvirt. -greenprint "πŸšͺ Allowing users in wheel group to talk to libvirt" -WHEEL_GROUP=wheel -if [[ $ID == rhel ]]; then - WHEEL_GROUP=adm -fi -sudo tee /etc/polkit-1/rules.d/50-libvirt.rules > /dev/null << EOF -polkit.addRule(function(action, subject) { - if (action.id == "org.libvirt.unix.manage" && - subject.isInGroup("${WHEEL_GROUP}")) { - return polkit.Result.YES; - } -}); -EOF - -# Set up variables. -TEST_UUID=$(uuidgen) -IMAGE_KEY=osbuild-composer-qemu-test-${TEST_UUID} -INSTANCE_ADDRESS=192.168.100.50 - -# Set up temporary files. -TEMPDIR=$(mktemp -d) -BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml -COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json -COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json - -# Check for the smoke test file on the AWS instance that we start. -smoke_test_check () { - # Ensure the ssh key has restricted permissions. - SSH_KEY=${OSBUILD_COMPOSER_TEST_DATA}keyring/id_rsa - - SSH_OPTIONS=(-o StrictHostKeyChecking=no -o ConnectTimeout=5) - SMOKE_TEST=$(sudo ssh "${SSH_OPTIONS[@]}" -i "${SSH_KEY}" redhat@"${1}" 'cat /etc/smoke-test.txt') - if [[ $SMOKE_TEST == smoke-test ]]; then - echo 1 - else - echo 0 - fi -} - -# Get the compose log. -get_compose_log () { - COMPOSE_ID=$1 - LOG_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.log - - # Download the logs. - sudo composer-cli compose log "$COMPOSE_ID" | tee "$LOG_FILE" > /dev/null -} - -# Get the compose metadata. -get_compose_metadata () { - COMPOSE_ID=$1 - METADATA_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.json - - # Download the metadata. - sudo composer-cli compose metadata "$COMPOSE_ID" > /dev/null - - # Find the tarball and extract it. - TARBALL=$(basename "$(find . -maxdepth 1 -type f -name "*-metadata.tar")") - tar -xf "$TARBALL" - rm -f "$TARBALL" - - # Move the JSON file into place. - cat "${COMPOSE_ID}".json | jq -M '.' | tee "$METADATA_FILE" > /dev/null -} - -# Write a basic blueprint for our image. -# NOTE(mhayden): The service customization will always be required for QCOW2 -# but it is needed for OpenStack due to issue #698 in osbuild-composer. 😭 -# NOTE(mhayden): The cloud-init package isn't included in VHD/Azure images -# by default and it must be added here. -tee "$BLUEPRINT_FILE" > /dev/null << EOF -name = "bash" -description = "A base system with bash" -version = "0.0.1" - -[[packages]] -name = "bash" - -[[packages]] -name = "cloud-init" - -[customizations.services] -enabled = ["sshd", "cloud-init", "cloud-init-local", "cloud-config", "cloud-final"] - -[customizations.kernel] -append = "LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0" -EOF - -# Prepare the blueprint for the compose. -greenprint "πŸ“‹ Preparing blueprint" -sudo composer-cli blueprints push "$BLUEPRINT_FILE" -sudo composer-cli blueprints depsolve bash - -# Get worker unit file so we can watch the journal. -WORKER_UNIT=$(sudo systemctl list-units | grep -o -E "osbuild.*worker.*\.service") -sudo journalctl -af -n 1 -u "${WORKER_UNIT}" & -WORKER_JOURNAL_PID=$! - -# Start the compose -greenprint "πŸš€ Starting compose" -sudo composer-cli --json compose start bash "$IMAGE_TYPE" | tee "$COMPOSE_START" -COMPOSE_ID=$(jq -r '.build_id' "$COMPOSE_START") - -# Wait for the compose to finish. -greenprint "⏱ Waiting for compose to finish: ${COMPOSE_ID}" -while true; do - sudo composer-cli --json compose info "${COMPOSE_ID}" | tee "$COMPOSE_INFO" > /dev/null - COMPOSE_STATUS=$(jq -r '.queue_status' "$COMPOSE_INFO") - - # Is the compose finished? - if [[ $COMPOSE_STATUS != RUNNING ]] && [[ $COMPOSE_STATUS != WAITING ]]; then - break - fi - - # Wait 30 seconds and try again. - sleep 5 -done - -# Capture the compose logs from osbuild. -greenprint "πŸ’¬ Getting compose log and metadata" -get_compose_log "$COMPOSE_ID" -get_compose_metadata "$COMPOSE_ID" - -# Did the compose finish with success? -if [[ $COMPOSE_STATUS != FINISHED ]]; then - echo "Something went wrong with the compose. 😒" - exit 1 -fi - -# Stop watching the worker journal. -sudo kill ${WORKER_JOURNAL_PID} - -# Download the image. -greenprint "πŸ“₯ Downloading the image" - -# Current $PWD is inside /tmp, there may not be enough space for an image. -# Let's use a bigger temporary directory for this operation. -BIG_TEMP_DIR=/var/lib/osbuild-composer-tests -sudo rm -rf "${BIG_TEMP_DIR}" || true -sudo mkdir "${BIG_TEMP_DIR}" -pushd "${BIG_TEMP_DIR}" - sudo composer-cli compose image "${COMPOSE_ID}" > /dev/null - IMAGE_FILENAME=$(basename "$(find . -maxdepth 1 -type f -name "*.${IMAGE_EXTENSION}")") - LIBVIRT_IMAGE_PATH=/var/lib/libvirt/images/${IMAGE_KEY}.${IMAGE_EXTENSION} - sudo mv "$IMAGE_FILENAME" "$LIBVIRT_IMAGE_PATH" -popd - -# Prepare cloud-init data. -CLOUD_INIT_DIR=$(mktemp -d) -cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/{meta,user}-data "${CLOUD_INIT_DIR}"/ -cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/network-config "${CLOUD_INIT_DIR}"/ - -# Set up a cloud-init ISO. -greenprint "πŸ’Ώ Creating a cloud-init ISO" -CLOUD_INIT_PATH=/var/lib/libvirt/images/seed.iso -rm -f $CLOUD_INIT_PATH -pushd "$CLOUD_INIT_DIR" - sudo genisoimage -o $CLOUD_INIT_PATH -V cidata \ - -r -J user-data meta-data network-config > /dev/null 2>&1 -popd - -# Ensure SELinux is happy with our new images. -greenprint "πŸ‘Ώ Running restorecon on image directory" -sudo restorecon -Rv /var/lib/libvirt/images/ - -# Run virt-install to import the QCOW and boot it. -greenprint "πŸš€ Booting the image with libvirt" -if [[ $ARCH == 'ppc64le' ]]; then - # ppc64le has some machine quirks that must be worked around. - sudo virt-install \ - --name "$IMAGE_KEY" \ - --memory 2048 \ - --vcpus 2 \ - --disk path="${LIBVIRT_IMAGE_PATH}" \ - --disk path=${CLOUD_INIT_PATH},device=cdrom \ - --import \ - --os-variant rhel8-unknown \ - --noautoconsole \ - --network network=integration,mac=34:49:22:B0:83:30 \ - --qemu-commandline="-machine pseries,cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off,cap-large-decr=off" -elif [[ $ARCH == 's390x' ]]; then - # Our s390x machines are highly constrained on resources. - sudo virt-install \ - --name "$IMAGE_KEY" \ - --memory 512 \ - --vcpus 1 \ - --disk path="${LIBVIRT_IMAGE_PATH}" \ - --disk path=${CLOUD_INIT_PATH},device=cdrom \ - --import \ - --os-variant rhel8-unknown \ - --noautoconsole \ - --network network=integration,mac=34:49:22:B0:83:30 -else - # Both aarch64 and x86_64 support hybrid boot - if [[ $BOOT_TYPE == 'uefi' ]]; then - sudo virt-install \ - --name "$IMAGE_KEY" \ - --memory 1024 \ - --vcpus 2 \ - --disk path="${LIBVIRT_IMAGE_PATH}" \ - --disk path=${CLOUD_INIT_PATH},device=cdrom \ - --import \ - --os-variant rhel8-unknown \ - --noautoconsole \ - --boot uefi,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd \ - --network network=integration,mac=34:49:22:B0:83:30 - else - sudo virt-install \ - --name "$IMAGE_KEY" \ - --memory 1024 \ - --vcpus 2 \ - --disk path="${LIBVIRT_IMAGE_PATH}" \ - --disk path=${CLOUD_INIT_PATH},device=cdrom \ - --import \ - --os-variant rhel8-unknown \ - --noautoconsole \ - --network network=integration,mac=34:49:22:B0:83:30 - fi -fi - -# Set a number of maximum loops to check for our smoke test file via ssh. -case $ARCH in - s390x) - # s390x needs more time to boot its VM. - MAX_LOOPS=60 - ;; - *) - MAX_LOOPS=30 - ;; -esac - -# Check for our smoke test file. -greenprint "πŸ›ƒ Checking for smoke test file in VM" -# shellcheck disable=SC2034 # Unused variables left for readability -for LOOP_COUNTER in $(seq 0 ${MAX_LOOPS}); do - RESULTS="$(smoke_test_check $INSTANCE_ADDRESS)" - if [[ $RESULTS == 1 ]]; then - echo "Smoke test passed! πŸ₯³" - break - fi - sleep 10 -done - -# Clean up our mess. -greenprint "🧼 Cleaning up" -sudo virsh destroy "${IMAGE_KEY}" -if [[ $ARCH == aarch64 || $BOOT_TYPE == 'uefi' ]]; then - sudo virsh undefine "${IMAGE_KEY}" --nvram -else - sudo virsh undefine "${IMAGE_KEY}" -fi -sudo rm -f "$LIBVIRT_IMAGE_PATH" $CLOUD_INIT_PATH - -# Also delete the compose so we don't run out of disk space -sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null - -# Use the return code of the smoke test to determine if we passed or failed. -if [[ $RESULTS == 1 ]]; then - greenprint "πŸ’š Success" -else - greenprint "❌ Failed" - exit 1 -fi - -exit 0 diff --git a/tools/libvirt_test.sh b/tools/libvirt_test.sh new file mode 100755 index 000000000..432cee154 --- /dev/null +++ b/tools/libvirt_test.sh @@ -0,0 +1,338 @@ +#!/bin/bash +set -euo pipefail + +OSBUILD_COMPOSER_TEST_DATA=/usr/share/tests/osbuild-composer/ + +# Get OS data. +source /etc/os-release +ARCH=$(uname -m) + +# Take the image type passed to the script or use qcow2 by default if nothing +# was passed. +IMAGE_TYPE=${1:-qcow2} +# Take the boot type passed to the script or use BIOS by default if nothing +# was passed. +BOOT_TYPE=${2:-bios} + +# Select the file extension based on the image that we are building. +IMAGE_EXTENSION=$IMAGE_TYPE +if [[ $IMAGE_TYPE == 'openstack' ]]; then + IMAGE_EXTENSION=qcow2 +fi + +# RHEL 8 cannot boot a VMDK using libvirt. See BZ 999789. +if [[ $IMAGE_TYPE == vmdk ]]; then + echo "🀷 libvirt cannot boot stream-optimized VMDK." + exit 0 +fi + +# Apply lorax patch to work around pytoml issues in RHEL 8.x. +# See BZ 1843704 or https://github.com/weldr/lorax/pull/1030 for more details. +if [[ $ID == rhel ]]; then + sudo sed -r -i 's#toml.load\(args\[3\]\)#toml.load(open(args[3]))#' \ + /usr/lib/python3.6/site-packages/composer/cli/compose.py + sudo rm -f /usr/lib/python3.6/site-packages/composer/cli/compose.pyc +fi + +# Colorful output. +function greenprint { + echo -e "\033[1;32m${1}\033[0m" +} + +# Start libvirtd and test it. +greenprint "πŸš€ Starting libvirt daemon" +sudo systemctl start libvirtd +sudo virsh list --all > /dev/null + +# Set a customized dnsmasq configuration for libvirt so we always get the +# same address on bootup. +sudo tee /tmp/integration.xml > /dev/null << EOF + + integration + 1c8fe98c-b53a-4ca4-bbdb-deb0f26b3579 + + + + + + + + + + + + + + +EOF +if ! sudo virsh net-info integration > /dev/null 2>&1; then + sudo virsh net-define /tmp/integration.xml + sudo virsh net-start integration +fi + +# Allow anyone in the wheel group to talk to libvirt. +greenprint "πŸšͺ Allowing users in wheel group to talk to libvirt" +WHEEL_GROUP=wheel +if [[ $ID == rhel ]]; then + WHEEL_GROUP=adm +fi +sudo tee /etc/polkit-1/rules.d/50-libvirt.rules > /dev/null << EOF +polkit.addRule(function(action, subject) { + if (action.id == "org.libvirt.unix.manage" && + subject.isInGroup("${WHEEL_GROUP}")) { + return polkit.Result.YES; + } +}); +EOF + +# Set up variables. +TEST_UUID=$(uuidgen) +IMAGE_KEY=osbuild-composer-qemu-test-${TEST_UUID} +INSTANCE_ADDRESS=192.168.100.50 + +# Set up temporary files. +TEMPDIR=$(mktemp -d) +BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml +COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json +COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json + +# Check for the smoke test file on the AWS instance that we start. +smoke_test_check () { + # Ensure the ssh key has restricted permissions. + SSH_KEY=${OSBUILD_COMPOSER_TEST_DATA}keyring/id_rsa + + SSH_OPTIONS=(-o StrictHostKeyChecking=no -o ConnectTimeout=5) + SMOKE_TEST=$(sudo ssh "${SSH_OPTIONS[@]}" -i "${SSH_KEY}" redhat@"${1}" 'cat /etc/smoke-test.txt') + if [[ $SMOKE_TEST == smoke-test ]]; then + echo 1 + else + echo 0 + fi +} + +# Get the compose log. +get_compose_log () { + COMPOSE_ID=$1 + LOG_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.log + + # Download the logs. + sudo composer-cli compose log "$COMPOSE_ID" | tee "$LOG_FILE" > /dev/null +} + +# Get the compose metadata. +get_compose_metadata () { + COMPOSE_ID=$1 + METADATA_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.json + + # Download the metadata. + sudo composer-cli compose metadata "$COMPOSE_ID" > /dev/null + + # Find the tarball and extract it. + TARBALL=$(basename "$(find . -maxdepth 1 -type f -name "*-metadata.tar")") + tar -xf "$TARBALL" + rm -f "$TARBALL" + + # Move the JSON file into place. + cat "${COMPOSE_ID}".json | jq -M '.' | tee "$METADATA_FILE" > /dev/null +} + +# Write a basic blueprint for our image. +# NOTE(mhayden): The service customization will always be required for QCOW2 +# but it is needed for OpenStack due to issue #698 in osbuild-composer. 😭 +# NOTE(mhayden): The cloud-init package isn't included in VHD/Azure images +# by default and it must be added here. +tee "$BLUEPRINT_FILE" > /dev/null << EOF +name = "bash" +description = "A base system with bash" +version = "0.0.1" + +[[packages]] +name = "bash" + +[[packages]] +name = "cloud-init" + +[customizations.services] +enabled = ["sshd", "cloud-init", "cloud-init-local", "cloud-config", "cloud-final"] + +[customizations.kernel] +append = "LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0" +EOF + +# Prepare the blueprint for the compose. +greenprint "πŸ“‹ Preparing blueprint" +sudo composer-cli blueprints push "$BLUEPRINT_FILE" +sudo composer-cli blueprints depsolve bash + +# Get worker unit file so we can watch the journal. +WORKER_UNIT=$(sudo systemctl list-units | grep -o -E "osbuild.*worker.*\.service") +sudo journalctl -af -n 1 -u "${WORKER_UNIT}" & +WORKER_JOURNAL_PID=$! + +# Start the compose +greenprint "πŸš€ Starting compose" +sudo composer-cli --json compose start bash "$IMAGE_TYPE" | tee "$COMPOSE_START" +COMPOSE_ID=$(jq -r '.build_id' "$COMPOSE_START") + +# Wait for the compose to finish. +greenprint "⏱ Waiting for compose to finish: ${COMPOSE_ID}" +while true; do + sudo composer-cli --json compose info "${COMPOSE_ID}" | tee "$COMPOSE_INFO" > /dev/null + COMPOSE_STATUS=$(jq -r '.queue_status' "$COMPOSE_INFO") + + # Is the compose finished? + if [[ $COMPOSE_STATUS != RUNNING ]] && [[ $COMPOSE_STATUS != WAITING ]]; then + break + fi + + # Wait 30 seconds and try again. + sleep 5 +done + +# Capture the compose logs from osbuild. +greenprint "πŸ’¬ Getting compose log and metadata" +get_compose_log "$COMPOSE_ID" +get_compose_metadata "$COMPOSE_ID" + +# Did the compose finish with success? +if [[ $COMPOSE_STATUS != FINISHED ]]; then + echo "Something went wrong with the compose. 😒" + exit 1 +fi + +# Stop watching the worker journal. +sudo kill ${WORKER_JOURNAL_PID} + +# Download the image. +greenprint "πŸ“₯ Downloading the image" + +# Current $PWD is inside /tmp, there may not be enough space for an image. +# Let's use a bigger temporary directory for this operation. +BIG_TEMP_DIR=/var/lib/osbuild-composer-tests +sudo rm -rf "${BIG_TEMP_DIR}" || true +sudo mkdir "${BIG_TEMP_DIR}" +pushd "${BIG_TEMP_DIR}" + sudo composer-cli compose image "${COMPOSE_ID}" > /dev/null + IMAGE_FILENAME=$(basename "$(find . -maxdepth 1 -type f -name "*.${IMAGE_EXTENSION}")") + LIBVIRT_IMAGE_PATH=/var/lib/libvirt/images/${IMAGE_KEY}.${IMAGE_EXTENSION} + sudo mv "$IMAGE_FILENAME" "$LIBVIRT_IMAGE_PATH" +popd + +# Prepare cloud-init data. +CLOUD_INIT_DIR=$(mktemp -d) +cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/{meta,user}-data "${CLOUD_INIT_DIR}"/ +cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/network-config "${CLOUD_INIT_DIR}"/ + +# Set up a cloud-init ISO. +greenprint "πŸ’Ώ Creating a cloud-init ISO" +CLOUD_INIT_PATH=/var/lib/libvirt/images/seed.iso +rm -f $CLOUD_INIT_PATH +pushd "$CLOUD_INIT_DIR" + sudo genisoimage -o $CLOUD_INIT_PATH -V cidata \ + -r -J user-data meta-data network-config > /dev/null 2>&1 +popd + +# Ensure SELinux is happy with our new images. +greenprint "πŸ‘Ώ Running restorecon on image directory" +sudo restorecon -Rv /var/lib/libvirt/images/ + +# Run virt-install to import the QCOW and boot it. +greenprint "πŸš€ Booting the image with libvirt" +if [[ $ARCH == 'ppc64le' ]]; then + # ppc64le has some machine quirks that must be worked around. + sudo virt-install \ + --name "$IMAGE_KEY" \ + --memory 2048 \ + --vcpus 2 \ + --disk path="${LIBVIRT_IMAGE_PATH}" \ + --disk path=${CLOUD_INIT_PATH},device=cdrom \ + --import \ + --os-variant rhel8-unknown \ + --noautoconsole \ + --network network=integration,mac=34:49:22:B0:83:30 \ + --qemu-commandline="-machine pseries,cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off,cap-large-decr=off" +elif [[ $ARCH == 's390x' ]]; then + # Our s390x machines are highly constrained on resources. + sudo virt-install \ + --name "$IMAGE_KEY" \ + --memory 512 \ + --vcpus 1 \ + --disk path="${LIBVIRT_IMAGE_PATH}" \ + --disk path=${CLOUD_INIT_PATH},device=cdrom \ + --import \ + --os-variant rhel8-unknown \ + --noautoconsole \ + --network network=integration,mac=34:49:22:B0:83:30 +else + # Both aarch64 and x86_64 support hybrid boot + if [[ $BOOT_TYPE == 'uefi' ]]; then + sudo virt-install \ + --name "$IMAGE_KEY" \ + --memory 1024 \ + --vcpus 2 \ + --disk path="${LIBVIRT_IMAGE_PATH}" \ + --disk path=${CLOUD_INIT_PATH},device=cdrom \ + --import \ + --os-variant rhel8-unknown \ + --noautoconsole \ + --boot uefi,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd \ + --network network=integration,mac=34:49:22:B0:83:30 + else + sudo virt-install \ + --name "$IMAGE_KEY" \ + --memory 1024 \ + --vcpus 2 \ + --disk path="${LIBVIRT_IMAGE_PATH}" \ + --disk path=${CLOUD_INIT_PATH},device=cdrom \ + --import \ + --os-variant rhel8-unknown \ + --noautoconsole \ + --network network=integration,mac=34:49:22:B0:83:30 + fi +fi + +# Set a number of maximum loops to check for our smoke test file via ssh. +case $ARCH in + s390x) + # s390x needs more time to boot its VM. + MAX_LOOPS=60 + ;; + *) + MAX_LOOPS=30 + ;; +esac + +# Check for our smoke test file. +greenprint "πŸ›ƒ Checking for smoke test file in VM" +# shellcheck disable=SC2034 # Unused variables left for readability +for LOOP_COUNTER in $(seq 0 ${MAX_LOOPS}); do + RESULTS="$(smoke_test_check $INSTANCE_ADDRESS)" + if [[ $RESULTS == 1 ]]; then + echo "Smoke test passed! πŸ₯³" + break + fi + sleep 10 +done + +# Clean up our mess. +greenprint "🧼 Cleaning up" +sudo virsh destroy "${IMAGE_KEY}" +if [[ $ARCH == aarch64 || $BOOT_TYPE == 'uefi' ]]; then + sudo virsh undefine "${IMAGE_KEY}" --nvram +else + sudo virsh undefine "${IMAGE_KEY}" +fi +sudo rm -f "$LIBVIRT_IMAGE_PATH" $CLOUD_INIT_PATH + +# Also delete the compose so we don't run out of disk space +sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null + +# Use the return code of the smoke test to determine if we passed or failed. +if [[ $RESULTS == 1 ]]; then + greenprint "πŸ’š Success" +else + greenprint "❌ Failed" + exit 1 +fi + +exit 0