Blame tools/libvirt_test.sh

Packit Service 509fd4
#!/bin/bash
Packit Service 509fd4
set -euo pipefail
Packit Service 509fd4
Packit Service 509fd4
OSBUILD_COMPOSER_TEST_DATA=/usr/share/tests/osbuild-composer/
Packit Service 509fd4
Packit Service 509fd4
# Get OS data.
Packit Service 509fd4
source /etc/os-release
Packit Service 509fd4
ARCH=$(uname -m)
Packit Service 509fd4
Packit Service 509fd4
# Take the image type passed to the script or use qcow2 by default if nothing
Packit Service 509fd4
# was passed.
Packit Service 509fd4
IMAGE_TYPE=${1:-qcow2}
Packit Service 509fd4
# Take the boot type passed to the script or use BIOS by default if nothing
Packit Service 509fd4
# was passed.
Packit Service 509fd4
BOOT_TYPE=${2:-bios}
Packit Service 509fd4
Packit Service 509fd4
# Select the file extension based on the image that we are building.
Packit Service 509fd4
IMAGE_EXTENSION=$IMAGE_TYPE
Packit Service 509fd4
if [[ $IMAGE_TYPE == 'openstack' ]]; then
Packit Service 509fd4
    IMAGE_EXTENSION=qcow2
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# RHEL 8 cannot boot a VMDK using libvirt. See BZ 999789.
Packit Service 509fd4
if [[ $IMAGE_TYPE == vmdk ]]; then
Packit Service 509fd4
    echo "๐Ÿคท libvirt cannot boot stream-optimized VMDK."
Packit Service 509fd4
    exit 0
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# Apply lorax patch to work around pytoml issues in RHEL 8.x.
Packit Service 509fd4
# See BZ 1843704 or https://github.com/weldr/lorax/pull/1030 for more details.
Packit Service 509fd4
if [[ $ID == rhel ]]; then
Packit Service 509fd4
    sudo sed -r -i 's#toml.load\(args\[3\]\)#toml.load(open(args[3]))#' \
Packit Service 509fd4
        /usr/lib/python3.6/site-packages/composer/cli/compose.py
Packit Service 509fd4
    sudo rm -f /usr/lib/python3.6/site-packages/composer/cli/compose.pyc
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# Colorful output.
Packit Service 509fd4
function greenprint {
Packit Service 509fd4
    echo -e "\033[1;32m${1}\033[0m"
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
# Start libvirtd and test it.
Packit Service 509fd4
greenprint "๐Ÿš€ Starting libvirt daemon"
Packit Service 509fd4
sudo systemctl start libvirtd
Packit Service 509fd4
sudo virsh list --all > /dev/null
Packit Service 509fd4
Packit Service 509fd4
# Set a customized dnsmasq configuration for libvirt so we always get the
Packit Service 509fd4
# same address on bootup.
Packit Service 509fd4
sudo tee /tmp/integration.xml > /dev/null << EOF
Packit Service 509fd4
<network>
Packit Service 509fd4
  <name>integration</name>
Packit Service 509fd4
  <uuid>1c8fe98c-b53a-4ca4-bbdb-deb0f26b3579</uuid>
Packit Service 509fd4
  <forward mode='nat'>
Packit Service 509fd4
    <nat>
Packit Service 509fd4
      <port start='1024' end='65535'/>
Packit Service 509fd4
    </nat>
Packit Service 509fd4
  </forward>
Packit Service 509fd4
  <bridge name='integration' stp='on' delay='0'/>
Packit Service 509fd4
  <mac address='52:54:00:36:46:ef'/>
Packit Service 509fd4
  <ip address='192.168.100.1' netmask='255.255.255.0'>
Packit Service 509fd4
    <dhcp>
Packit Service 509fd4
      <range start='192.168.100.2' end='192.168.100.254'/>
Packit Service 509fd4
      <host mac='34:49:22:B0:83:30' name='vm' ip='192.168.100.50'/>
Packit Service 509fd4
    </dhcp>
Packit Service 509fd4
  </ip>
Packit Service 509fd4
</network>
Packit Service 509fd4
EOF
Packit Service 509fd4
if ! sudo virsh net-info integration > /dev/null 2>&1; then
Packit Service 509fd4
    sudo virsh net-define /tmp/integration.xml
Packit Service 509fd4
    sudo virsh net-start integration
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# Allow anyone in the wheel group to talk to libvirt.
Packit Service 509fd4
greenprint "๐Ÿšช Allowing users in wheel group to talk to libvirt"
Packit Service 509fd4
WHEEL_GROUP=wheel
Packit Service 509fd4
if [[ $ID == rhel ]]; then
Packit Service 509fd4
    WHEEL_GROUP=adm
Packit Service 509fd4
fi
Packit Service 509fd4
sudo tee /etc/polkit-1/rules.d/50-libvirt.rules > /dev/null << EOF
Packit Service 509fd4
polkit.addRule(function(action, subject) {
Packit Service 509fd4
    if (action.id == "org.libvirt.unix.manage" &&
Packit Service 509fd4
        subject.isInGroup("${WHEEL_GROUP}")) {
Packit Service 509fd4
            return polkit.Result.YES;
Packit Service 509fd4
    }
Packit Service 509fd4
});
Packit Service 509fd4
EOF
Packit Service 509fd4
Packit Service 509fd4
# Set up variables.
Packit Service 509fd4
TEST_UUID=$(uuidgen)
Packit Service 509fd4
IMAGE_KEY=osbuild-composer-qemu-test-${TEST_UUID}
Packit Service 509fd4
INSTANCE_ADDRESS=192.168.100.50
Packit Service 509fd4
Packit Service 509fd4
# Set up temporary files.
Packit Service 509fd4
TEMPDIR=$(mktemp -d)
Packit Service 509fd4
BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml
Packit Service 509fd4
COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json
Packit Service 509fd4
COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json
Packit Service 509fd4
Packit Service 509fd4
# Check for the smoke test file on the AWS instance that we start.
Packit Service 509fd4
smoke_test_check () {
Packit Service 509fd4
    # Ensure the ssh key has restricted permissions.
Packit Service 509fd4
    SSH_KEY=${OSBUILD_COMPOSER_TEST_DATA}keyring/id_rsa
Packit Service 509fd4
Packit Service 509fd4
    SSH_OPTIONS=(-o StrictHostKeyChecking=no -o ConnectTimeout=5)
Packit Service 509fd4
    SMOKE_TEST=$(sudo ssh "${SSH_OPTIONS[@]}" -i "${SSH_KEY}" redhat@"${1}" 'cat /etc/smoke-test.txt')
Packit Service 509fd4
    if [[ $SMOKE_TEST == smoke-test ]]; then
Packit Service 509fd4
        echo 1
Packit Service 509fd4
    else
Packit Service 509fd4
        echo 0
Packit Service 509fd4
    fi
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
# Get the compose log.
Packit Service 509fd4
get_compose_log () {
Packit Service 509fd4
    COMPOSE_ID=$1
Packit Service 509fd4
    LOG_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.log
Packit Service 509fd4
Packit Service 509fd4
    # Download the logs.
Packit Service 509fd4
    sudo composer-cli compose log "$COMPOSE_ID" | tee "$LOG_FILE" > /dev/null
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
# Get the compose metadata.
Packit Service 509fd4
get_compose_metadata () {
Packit Service 509fd4
    COMPOSE_ID=$1
Packit Service 509fd4
    METADATA_FILE=${WORKSPACE}/osbuild-${ID}-${VERSION_ID}-${IMAGE_TYPE}.json
Packit Service 509fd4
Packit Service 509fd4
    # Download the metadata.
Packit Service 509fd4
    sudo composer-cli compose metadata "$COMPOSE_ID" > /dev/null
Packit Service 509fd4
Packit Service 509fd4
    # Find the tarball and extract it.
Packit Service 509fd4
    TARBALL=$(basename "$(find . -maxdepth 1 -type f -name "*-metadata.tar")")
Packit Service 509fd4
    tar -xf "$TARBALL"
Packit Service 509fd4
    rm -f "$TARBALL"
Packit Service 509fd4
Packit Service 509fd4
    # Move the JSON file into place.
Packit Service 509fd4
    cat "${COMPOSE_ID}".json | jq -M '.' | tee "$METADATA_FILE" > /dev/null
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
# Write a basic blueprint for our image.
Packit Service 509fd4
# NOTE(mhayden): The service customization will always be required for QCOW2
Packit Service 509fd4
# but it is needed for OpenStack due to issue #698 in osbuild-composer. ๐Ÿ˜ญ
Packit Service 509fd4
# NOTE(mhayden): The cloud-init package isn't included in VHD/Azure images
Packit Service 509fd4
# by default and it must be added here.
Packit Service 509fd4
tee "$BLUEPRINT_FILE" > /dev/null << EOF
Packit Service 509fd4
name = "bash"
Packit Service 509fd4
description = "A base system with bash"
Packit Service 509fd4
version = "0.0.1"
Packit Service 509fd4
Packit Service 509fd4
[[packages]]
Packit Service 509fd4
name = "bash"
Packit Service 509fd4
Packit Service 509fd4
[[packages]]
Packit Service 509fd4
name = "cloud-init"
Packit Service 509fd4
Packit Service 509fd4
[customizations.services]
Packit Service 509fd4
enabled = ["sshd", "cloud-init", "cloud-init-local", "cloud-config", "cloud-final"]
Packit Service 509fd4
Packit Service 509fd4
[customizations.kernel]
Packit Service 509fd4
append = "LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0"
Packit Service 509fd4
EOF
Packit Service 509fd4
Packit Service 509fd4
# Prepare the blueprint for the compose.
Packit Service 509fd4
greenprint "๐Ÿ“‹ Preparing blueprint"
Packit Service 509fd4
sudo composer-cli blueprints push "$BLUEPRINT_FILE"
Packit Service 509fd4
sudo composer-cli blueprints depsolve bash
Packit Service 509fd4
Packit Service 509fd4
# Get worker unit file so we can watch the journal.
Packit Service 509fd4
WORKER_UNIT=$(sudo systemctl list-units | grep -o -E "osbuild.*worker.*\.service")
Packit Service 509fd4
sudo journalctl -af -n 1 -u "${WORKER_UNIT}" &
Packit Service 509fd4
WORKER_JOURNAL_PID=$!
Packit Service 509fd4
Packit Service 509fd4
# Start the compose
Packit Service 509fd4
greenprint "๐Ÿš€ Starting compose"
Packit Service 509fd4
sudo composer-cli --json compose start bash "$IMAGE_TYPE" | tee "$COMPOSE_START"
Packit Service 509fd4
COMPOSE_ID=$(jq -r '.build_id' "$COMPOSE_START")
Packit Service 509fd4
Packit Service 509fd4
# Wait for the compose to finish.
Packit Service 509fd4
greenprint "โฑ Waiting for compose to finish: ${COMPOSE_ID}"
Packit Service 509fd4
while true; do
Packit Service 509fd4
    sudo composer-cli --json compose info "${COMPOSE_ID}" | tee "$COMPOSE_INFO" > /dev/null
Packit Service 509fd4
    COMPOSE_STATUS=$(jq -r '.queue_status' "$COMPOSE_INFO")
Packit Service 509fd4
Packit Service 509fd4
    # Is the compose finished?
Packit Service 509fd4
    if [[ $COMPOSE_STATUS != RUNNING ]] && [[ $COMPOSE_STATUS != WAITING ]]; then
Packit Service 509fd4
        break
Packit Service 509fd4
    fi
Packit Service 509fd4
Packit Service 509fd4
    # Wait 30 seconds and try again.
Packit Service 509fd4
    sleep 5
Packit Service 509fd4
done
Packit Service 509fd4
Packit Service 509fd4
# Capture the compose logs from osbuild.
Packit Service 509fd4
greenprint "๐Ÿ’ฌ Getting compose log and metadata"
Packit Service 509fd4
get_compose_log "$COMPOSE_ID"
Packit Service 509fd4
get_compose_metadata "$COMPOSE_ID"
Packit Service 509fd4
Packit Service 509fd4
# Did the compose finish with success?
Packit Service 509fd4
if [[ $COMPOSE_STATUS != FINISHED ]]; then
Packit Service 509fd4
    echo "Something went wrong with the compose. ๐Ÿ˜ข"
Packit Service 509fd4
    exit 1
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# Stop watching the worker journal.
Packit Service 509fd4
sudo kill ${WORKER_JOURNAL_PID}
Packit Service 509fd4
Packit Service 509fd4
# Download the image.
Packit Service 509fd4
greenprint "๐Ÿ“ฅ Downloading the image"
Packit Service 509fd4
Packit Service 509fd4
# Current $PWD is inside /tmp, there may not be enough space for an image.
Packit Service 509fd4
# Let's use a bigger temporary directory for this operation.
Packit Service 509fd4
BIG_TEMP_DIR=/var/lib/osbuild-composer-tests
Packit Service 509fd4
sudo rm -rf "${BIG_TEMP_DIR}" || true
Packit Service 509fd4
sudo mkdir "${BIG_TEMP_DIR}"
Packit Service 509fd4
pushd "${BIG_TEMP_DIR}"
Packit Service 509fd4
    sudo composer-cli compose image "${COMPOSE_ID}" > /dev/null
Packit Service 509fd4
    IMAGE_FILENAME=$(basename "$(find . -maxdepth 1 -type f -name "*.${IMAGE_EXTENSION}")")
Packit Service 509fd4
    LIBVIRT_IMAGE_PATH=/var/lib/libvirt/images/${IMAGE_KEY}.${IMAGE_EXTENSION}
Packit Service 509fd4
    sudo mv "$IMAGE_FILENAME" "$LIBVIRT_IMAGE_PATH"
Packit Service 509fd4
popd
Packit Service 509fd4
Packit Service 509fd4
# Prepare cloud-init data.
Packit Service 509fd4
CLOUD_INIT_DIR=$(mktemp -d)
Packit Service 509fd4
cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/{meta,user}-data "${CLOUD_INIT_DIR}"/
Packit Service 509fd4
cp "${OSBUILD_COMPOSER_TEST_DATA}"/cloud-init/network-config "${CLOUD_INIT_DIR}"/
Packit Service 509fd4
Packit Service 509fd4
# Set up a cloud-init ISO.
Packit Service 509fd4
greenprint "๐Ÿ’ฟ Creating a cloud-init ISO"
Packit Service 509fd4
CLOUD_INIT_PATH=/var/lib/libvirt/images/seed.iso
Packit Service 509fd4
rm -f $CLOUD_INIT_PATH
Packit Service 509fd4
pushd "$CLOUD_INIT_DIR"
Packit Service 509fd4
    sudo genisoimage -o $CLOUD_INIT_PATH -V cidata \
Packit Service 509fd4
        -r -J user-data meta-data network-config > /dev/null 2>&1
Packit Service 509fd4
popd
Packit Service 509fd4
Packit Service 509fd4
# Ensure SELinux is happy with our new images.
Packit Service 509fd4
greenprint "๐Ÿ‘ฟ Running restorecon on image directory"
Packit Service 509fd4
sudo restorecon -Rv /var/lib/libvirt/images/
Packit Service 509fd4
Packit Service 509fd4
# Run virt-install to import the QCOW and boot it.
Packit Service 509fd4
greenprint "๐Ÿš€ Booting the image with libvirt"
Packit Service 509fd4
if [[ $ARCH == 'ppc64le' ]]; then
Packit Service 509fd4
    # ppc64le has some machine quirks that must be worked around.
Packit Service 509fd4
    sudo virt-install \
Packit Service 509fd4
        --name "$IMAGE_KEY" \
Packit Service 509fd4
        --memory 2048 \
Packit Service 509fd4
        --vcpus 2 \
Packit Service 509fd4
        --disk path="${LIBVIRT_IMAGE_PATH}" \
Packit Service 509fd4
        --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit Service 509fd4
        --import \
Packit Service 509fd4
        --os-variant rhel8-unknown \
Packit Service 509fd4
        --noautoconsole \
Packit Service 509fd4
        --network network=integration,mac=34:49:22:B0:83:30 \
Packit Service 509fd4
        --qemu-commandline="-machine pseries,cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off,cap-large-decr=off"
Packit Service 509fd4
elif [[ $ARCH == 's390x' ]]; then
Packit Service 509fd4
    # Our s390x machines are highly constrained on resources.
Packit Service 509fd4
    sudo virt-install \
Packit Service 509fd4
        --name "$IMAGE_KEY" \
Packit Service 509fd4
        --memory 512 \
Packit Service 509fd4
        --vcpus 1 \
Packit Service 509fd4
        --disk path="${LIBVIRT_IMAGE_PATH}" \
Packit Service 509fd4
        --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit Service 509fd4
        --import \
Packit Service 509fd4
        --os-variant rhel8-unknown \
Packit Service 509fd4
        --noautoconsole \
Packit Service 509fd4
        --network network=integration,mac=34:49:22:B0:83:30
Packit Service 509fd4
else
Packit Service 509fd4
    # Both aarch64 and x86_64 support hybrid boot
Packit Service 509fd4
    if [[ $BOOT_TYPE == 'uefi' ]]; then
Packit Service 509fd4
        sudo virt-install \
Packit Service 509fd4
            --name "$IMAGE_KEY" \
Packit Service 509fd4
            --memory 1024 \
Packit Service 509fd4
            --vcpus 2 \
Packit Service 509fd4
            --disk path="${LIBVIRT_IMAGE_PATH}" \
Packit Service 509fd4
            --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit Service 509fd4
            --import \
Packit Service 509fd4
            --os-variant rhel8-unknown \
Packit Service 509fd4
            --noautoconsole \
Packit Service 509fd4
            --boot uefi,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd \
Packit Service 509fd4
            --network network=integration,mac=34:49:22:B0:83:30
Packit Service 509fd4
    else
Packit Service 509fd4
        sudo virt-install \
Packit Service 509fd4
            --name "$IMAGE_KEY" \
Packit Service 509fd4
            --memory 1024 \
Packit Service 509fd4
            --vcpus 2 \
Packit Service 509fd4
            --disk path="${LIBVIRT_IMAGE_PATH}" \
Packit Service 509fd4
            --disk path=${CLOUD_INIT_PATH},device=cdrom \
Packit Service 509fd4
            --import \
Packit Service 509fd4
            --os-variant rhel8-unknown \
Packit Service 509fd4
            --noautoconsole \
Packit Service 509fd4
            --network network=integration,mac=34:49:22:B0:83:30
Packit Service 509fd4
    fi
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
# Set a number of maximum loops to check for our smoke test file via ssh.
Packit Service 509fd4
case $ARCH in
Packit Service 509fd4
    s390x)
Packit Service 509fd4
        # s390x needs more time to boot its VM.
Packit Service 509fd4
        MAX_LOOPS=60
Packit Service 509fd4
        ;;
Packit Service 509fd4
    *)
Packit Service 509fd4
        MAX_LOOPS=30
Packit Service 509fd4
        ;;
Packit Service 509fd4
esac
Packit Service 509fd4
Packit Service 509fd4
# Check for our smoke test file.
Packit Service 509fd4
greenprint "๐Ÿ›ƒ Checking for smoke test file in VM"
Packit Service 509fd4
# shellcheck disable=SC2034  # Unused variables left for readability
Packit Service 509fd4
for LOOP_COUNTER in $(seq 0 ${MAX_LOOPS}); do
Packit Service 509fd4
    RESULTS="$(smoke_test_check $INSTANCE_ADDRESS)"
Packit Service 509fd4
    if [[ $RESULTS == 1 ]]; then
Packit Service 509fd4
        echo "Smoke test passed! ๐Ÿฅณ"
Packit Service 509fd4
        break
Packit Service 509fd4
    fi
Packit Service 509fd4
    sleep 10
Packit Service 509fd4
done
Packit Service 509fd4
Packit Service 509fd4
# Clean up our mess.
Packit Service 509fd4
greenprint "๐Ÿงผ Cleaning up"
Packit Service 509fd4
sudo virsh destroy "${IMAGE_KEY}"
Packit Service 509fd4
if [[ $ARCH == aarch64 || $BOOT_TYPE == 'uefi' ]]; then
Packit Service 509fd4
    sudo virsh undefine "${IMAGE_KEY}" --nvram
Packit Service 509fd4
else
Packit Service 509fd4
    sudo virsh undefine "${IMAGE_KEY}"
Packit Service 509fd4
fi
Packit Service 509fd4
sudo rm -f "$LIBVIRT_IMAGE_PATH" $CLOUD_INIT_PATH
Packit Service 509fd4
Packit Service 509fd4
# Also delete the compose so we don't run out of disk space
Packit Service 509fd4
sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null
Packit Service 509fd4
Packit Service 509fd4
# Use the return code of the smoke test to determine if we passed or failed.
Packit Service 509fd4
if [[ $RESULTS == 1 ]]; then
Packit Service 509fd4
  greenprint "๐Ÿ’š Success"
Packit Service 509fd4
else
Packit Service 509fd4
  greenprint "โŒ Failed"
Packit Service 509fd4
  exit 1
Packit Service 509fd4
fi
Packit Service 509fd4
Packit Service 509fd4
exit 0