mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
* tests: properly build snapd snap Now we build also the test version of snapd snap in `snap-builds` workflow job. We copy this into the spread tests. And we use that snap, which we only instrument instead of copying the snapd deb build. If the snap is not available, then we build it in spread. On CI, this happens on arm since the workflow does not build it. It will also happen when triggering test manually. * tests: couple of small improvements to test syntax, move WORK_DIR into script scope, use PWD instead of dot notation --------- Co-authored-by: Philip Meulengracht <the_meulengracht@hotmail.com>
1733 lines
59 KiB
Bash
Executable File
1733 lines
59 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
: "${NESTED_WORK_DIR:=/tmp/work-dir}"
|
|
: "${NESTED_IMAGES_DIR:=${NESTED_WORK_DIR}/images}"
|
|
: "${NESTED_RUNTIME_DIR:=${NESTED_WORK_DIR}/runtime}"
|
|
: "${NESTED_ASSETS_DIR:=${NESTED_WORK_DIR}/assets}"
|
|
: "${NESTED_LOGS_DIR:=${NESTED_WORK_DIR}/logs}"
|
|
: "${NESTED_ARCHITECTURE:=amd64}"
|
|
|
|
: "${NESTED_VM:=nested-vm}"
|
|
: "${NESTED_SSH_PORT:=8022}"
|
|
: "${NESTED_MON_PORT:=8888}"
|
|
|
|
: "${NESTED_CUSTOM_MODEL:=}"
|
|
: "${NESTED_CUSTOM_AUTO_IMPORT_ASSERTION:=}"
|
|
: "${NESTED_FAKESTORE_BLOB_DIR:=${NESTED_WORK_DIR}/fakestore/blobs}"
|
|
: "${NESTED_SIGN_SNAPS_FAKESTORE:=false}"
|
|
: "${NESTED_FAKESTORE_SNAP_DECL_PC_GADGET:=}"
|
|
: "${NESTED_UBUNTU_IMAGE_SNAPPY_FORCE_SAS_URL:=}"
|
|
: "${NESTED_UBUNTU_IMAGE_PRESEED_KEY:=}"
|
|
|
|
: "${NESTED_DISK_PHYSICAL_BLOCK_SIZE:=512}"
|
|
: "${NESTED_DISK_LOGICAL_BLOCK_SIZE:=512}"
|
|
|
|
nested_wait_for_ssh() {
|
|
local retry=${1:-800}
|
|
local wait=${2:-1}
|
|
|
|
until remote.exec "true" &>/dev/null; do
|
|
if [ "$retry" -le 0 ]; then
|
|
return 1
|
|
fi
|
|
retry=$(( retry - 1 ))
|
|
sleep "$wait"
|
|
done
|
|
}
|
|
|
|
nested_wait_for_no_ssh() {
|
|
local retry=${1:-200}
|
|
local wait=${2:-1}
|
|
|
|
while remote.exec "true" &>/dev/null; do
|
|
if [ "$retry" -le 0 ]; then
|
|
return 1
|
|
fi
|
|
retry=$(( retry - 1 ))
|
|
sleep "$wait"
|
|
done
|
|
}
|
|
|
|
nested_wait_vm_ready() {
|
|
echo "Waiting the vm is ready to be used"
|
|
local retry=${1:-120}
|
|
|
|
local serial_log="$NESTED_LOGS_DIR"/serial.log
|
|
while true; do
|
|
# Check the timeout is reached
|
|
if [ "$retry" -le 0 ]; then
|
|
echo "Timed out waiting for vm ready. Aborting!"
|
|
return 1
|
|
fi
|
|
retry=$(( retry - 1 ))
|
|
|
|
# Check the vm is active
|
|
if ! systemctl is-active "$NESTED_VM"; then
|
|
echo "Unit $NESTED_VM is not active. Aborting!"
|
|
return 1
|
|
fi
|
|
|
|
# Check if ssh connection can be established, and return if it is possible
|
|
if nested_wait_for_ssh 1 1; then
|
|
echo "SSH connection ready"
|
|
return
|
|
fi
|
|
|
|
# Check no infinite loops during boot
|
|
if nested_is_core_ge 20; then
|
|
test "$(grep -c -E "Command line:.*snapd_recovery_mode=install" "$serial_log")" -le 1
|
|
test "$(grep -c -E "Command line:.*snapd_recovery_mode=run" "$serial_log")" -le 1
|
|
else
|
|
test "$(grep -c -E "Command line:.*BOOT_IMAGE=\(loop\)/kernel.img" "$serial_log")" -le 1
|
|
fi
|
|
|
|
sleep 3
|
|
done
|
|
|
|
nested_check_unit_stays_active "$NESTED_VM" 2 1
|
|
}
|
|
|
|
nested_wait_for_snap_command() {
|
|
# In this function the remote retry command cannot be used because it could
|
|
# be executed before the tool is deployed.
|
|
local retry=${1:-200}
|
|
local wait=${2:-1}
|
|
|
|
while ! remote.exec "command -v snap" &>/dev/null; do
|
|
if [ "$retry" -le 0 ]; then
|
|
echo "Timed out waiting for command 'command -v snap' to success. Aborting!"
|
|
return 1
|
|
fi
|
|
retry=$(( retry - 1 ))
|
|
sleep "$wait"
|
|
done
|
|
}
|
|
|
|
nested_check_unit_stays_active() {
|
|
local nested_unit="${1:-$NESTED_VM}"
|
|
local retry=${2:-5}
|
|
local wait=${3:-1}
|
|
|
|
while [ "$retry" -ge 0 ]; do
|
|
retry=$(( retry - 1 ))
|
|
|
|
if ! systemctl is-active "$nested_unit"; then
|
|
echo "Unit $nested_unit is not active. Aborting!"
|
|
return 1
|
|
fi
|
|
sleep "$wait"
|
|
done
|
|
}
|
|
|
|
nested_get_boot_id() {
|
|
remote.exec "cat /proc/sys/kernel/random/boot_id"
|
|
}
|
|
|
|
nested_wait_for_reboot() {
|
|
local initial_boot_id="$1"
|
|
local last_boot_id="$initial_boot_id"
|
|
local retry=150
|
|
local wait=5
|
|
|
|
while [ $retry -ge 0 ]; do
|
|
retry=$(( retry - 1 ))
|
|
# The get_boot_id could fail because the connection is broken due to the reboot
|
|
last_boot_id="$(nested_get_boot_id)" || true
|
|
if [[ "$last_boot_id" =~ .*-.*-.*-.*-.* ]] && [ "$last_boot_id" != "$initial_boot_id" ]; then
|
|
break
|
|
fi
|
|
sleep "$wait"
|
|
done
|
|
|
|
[ "$last_boot_id" != "$initial_boot_id" ]
|
|
}
|
|
|
|
nested_uc20_transition_to_system_mode() {
|
|
local recovery_system="$1"
|
|
local mode="$2"
|
|
|
|
if nested_is_core_le 18; then
|
|
echo "Transition can be done just on uc20+ systems, exiting..."
|
|
exit 1
|
|
fi
|
|
|
|
local current_boot_id
|
|
current_boot_id=$(nested_get_boot_id)
|
|
remote.exec "sudo snap reboot --$mode $recovery_system"
|
|
nested_wait_for_reboot "$current_boot_id"
|
|
|
|
# verify we are now in the requested mode
|
|
if ! remote.exec "cat /proc/cmdline" | MATCH "snapd_recovery_mode=$mode"; then
|
|
return 1
|
|
fi
|
|
|
|
# Copy tools to be used on tests
|
|
nested_prepare_tools
|
|
}
|
|
|
|
nested_prepare_ssh() {
|
|
if nested_is_core_ge 24; then
|
|
remote.exec "sudo useradd --uid 12345 --create-home --extrausers test"
|
|
remote.exec "sudo useradd --create-home --extrausers external"
|
|
else
|
|
remote.exec "sudo adduser --uid 12345 --extrausers --quiet --disabled-password --gecos '' test"
|
|
remote.exec "sudo adduser --extrausers --quiet --disabled-password --gecos '' external"
|
|
fi
|
|
|
|
remote.exec "echo test:ubuntu123 | sudo chpasswd"
|
|
remote.exec "echo 'test ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/create-user-test"
|
|
# Check we can connect with the new test user and make sudo
|
|
remote.exec --user test --pass ubuntu123 "sudo true"
|
|
|
|
remote.exec "echo external:ubuntu123 | sudo chpasswd"
|
|
remote.exec "echo 'external ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/create-user-external"
|
|
# Check we can connect with the new external user and make sudo
|
|
remote.exec --user external --pass ubuntu123 "sudo true"
|
|
}
|
|
|
|
|
|
nested_is_kvm_enabled() {
|
|
if [ -n "$NESTED_ENABLE_KVM" ]; then
|
|
[ "$NESTED_ENABLE_KVM" = true ]
|
|
fi
|
|
return 0
|
|
|
|
}
|
|
|
|
nested_is_tpm_enabled() {
|
|
if [ -n "$NESTED_ENABLE_TPM" ]; then
|
|
[ "$NESTED_ENABLE_TPM" = true ]
|
|
else
|
|
case "${SPREAD_SYSTEM:-}" in
|
|
ubuntu-1*)
|
|
return 1
|
|
;;
|
|
ubuntu-2*)
|
|
# TPM enabled by default on 20.04 and later
|
|
return 0
|
|
;;
|
|
*)
|
|
echo "unsupported system"
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
nested_is_secure_boot_enabled() {
|
|
if [ -n "$NESTED_ENABLE_SECURE_BOOT" ]; then
|
|
[ "$NESTED_ENABLE_SECURE_BOOT" = true ]
|
|
else
|
|
case "${SPREAD_SYSTEM:-}" in
|
|
ubuntu-1*)
|
|
return 1
|
|
;;
|
|
ubuntu-2*)
|
|
# secure boot enabled by default on 20.04 and later
|
|
return 0
|
|
;;
|
|
*)
|
|
echo "unsupported system"
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
nested_create_assertions_disk() {
|
|
mkdir -p "$NESTED_ASSETS_DIR"
|
|
local ASSERTIONS_DISK LOOP_DEV
|
|
ASSERTIONS_DISK="$NESTED_ASSETS_DIR/assertions.disk"
|
|
|
|
# make an image
|
|
dd if=/dev/null of="$ASSERTIONS_DISK" bs=1M seek=1
|
|
# format it as dos with a vfat partition
|
|
# TODO: can we do this more programmatically without printing into fdisk ?
|
|
printf 'o\nn\np\n1\n\n\nt\nc\nw\n' | fdisk "$ASSERTIONS_DISK"
|
|
# mount the disk image
|
|
kpartx -av "$ASSERTIONS_DISK"
|
|
# find the loopback device for the partition
|
|
LOOP_DEV=$(losetup --list | grep "$ASSERTIONS_DISK" | awk '{print $1}' | grep -Po "/dev/loop\K([0-9]*)")
|
|
# wait for the loop device to show up
|
|
retry -n 3 --wait 1 test -e "/dev/mapper/loop${LOOP_DEV}p1"
|
|
# make a vfat partition
|
|
mkfs.vfat -n SYSUSER "/dev/mapper/loop${LOOP_DEV}p1"
|
|
# mount the partition and copy the files
|
|
mkdir -p "$NESTED_ASSETS_DIR/sys-user-partition"
|
|
mount "/dev/mapper/loop${LOOP_DEV}p1" "$NESTED_ASSETS_DIR/sys-user-partition"
|
|
|
|
# use custom assertion if set
|
|
local AUTO_IMPORT_ASSERT
|
|
if [ -n "$NESTED_CUSTOM_AUTO_IMPORT_ASSERTION" ]; then
|
|
VERSION="$(nested_get_version)"
|
|
# shellcheck disable=SC2001
|
|
AUTO_IMPORT_ASSERT="$(echo "$NESTED_CUSTOM_AUTO_IMPORT_ASSERTION" | sed "s/{VERSION}/$VERSION/g")"
|
|
else
|
|
local per_model_auto
|
|
per_model_auto="$(nested_model_authority).auto-import.assert"
|
|
if [ -e "$TESTSLIB/assertions/${per_model_auto}" ]; then
|
|
AUTO_IMPORT_ASSERT="$TESTSLIB/assertions/${per_model_auto}"
|
|
else
|
|
AUTO_IMPORT_ASSERT="$TESTSLIB/assertions/auto-import.assert"
|
|
fi
|
|
fi
|
|
cp "$AUTO_IMPORT_ASSERT" "$NESTED_ASSETS_DIR/sys-user-partition/auto-import.assert"
|
|
|
|
# unmount the partition and the image disk
|
|
sudo umount "$NESTED_ASSETS_DIR/sys-user-partition"
|
|
sudo kpartx -d "$ASSERTIONS_DISK"
|
|
}
|
|
|
|
nested_qemu_name() {
|
|
if os.query is-arm; then
|
|
command -v qemu-system-aarch64
|
|
elif [ "$NESTED_ARCHITECTURE" = "i386" ]; then
|
|
command -v qemu-system-i386
|
|
else
|
|
command -v qemu-system-x86_64
|
|
fi
|
|
|
|
}
|
|
|
|
nested_get_snap_rev_for_channel() {
|
|
local SNAP=$1
|
|
local CHANNEL=$2
|
|
|
|
curl -s \
|
|
-H "Snap-Device-Architecture: $NESTED_ARCHITECTURE" \
|
|
-H "Snap-Device-Series: 16" \
|
|
-X POST \
|
|
-H "Content-Type: application/json" \
|
|
--data "{\"context\": [], \"actions\": [{\"action\": \"install\", \"name\": \"$SNAP\", \"channel\": \"$CHANNEL\", \"instance-key\": \"1\"}]}" \
|
|
https://api.snapcraft.io/v2/snaps/refresh | \
|
|
jq '.results[0].snap.revision'
|
|
}
|
|
|
|
nested_is_nested_system() {
|
|
if nested_is_core_system || nested_is_classic_system ; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
nested_is_core_system() {
|
|
if [ -z "${NESTED_TYPE:-}" ]; then
|
|
echo "Variable NESTED_TYPE not defined."
|
|
return 1
|
|
fi
|
|
|
|
test "$NESTED_TYPE" = "core"
|
|
}
|
|
|
|
nested_is_classic_system() {
|
|
if [ -z "${NESTED_TYPE:-}" ]; then
|
|
echo "Variable NESTED_TYPE not defined."
|
|
return 1
|
|
fi
|
|
|
|
test "$NESTED_TYPE" = "classic"
|
|
}
|
|
|
|
nested_is_core_ge() {
|
|
local VERSION=$1
|
|
os.query is-ubuntu-ge "${VERSION}.04"
|
|
}
|
|
|
|
nested_is_core_gt() {
|
|
local VERSION=$1
|
|
os.query is-ubuntu-gt "${VERSION}.04"
|
|
}
|
|
|
|
nested_is_core_le() {
|
|
local VERSION=$1
|
|
os.query is-ubuntu-le "${VERSION}.04"
|
|
}
|
|
|
|
nested_is_core_lt() {
|
|
local VERSION=$1
|
|
os.query is-ubuntu-lt "${VERSION}.04"
|
|
}
|
|
|
|
nested_is_core_24_system() {
|
|
os.query is-noble
|
|
}
|
|
|
|
nested_is_core_22_system() {
|
|
os.query is-jammy
|
|
}
|
|
|
|
nested_is_core_20_system() {
|
|
os.query is-focal
|
|
}
|
|
|
|
nested_is_core_18_system() {
|
|
os.query is-bionic
|
|
}
|
|
|
|
nested_is_core_16_system() {
|
|
os.query is-xenial
|
|
}
|
|
|
|
nested_refresh_to_new_core() {
|
|
local NEW_CHANNEL=$1
|
|
local CHANGE_ID
|
|
if [ "$NEW_CHANNEL" = "" ]; then
|
|
echo "Channel to refresh is not defined."
|
|
exit 1
|
|
else
|
|
echo "Refreshing the core/snapd snap"
|
|
if nested_is_classic_nested_system; then
|
|
remote.exec "sudo snap refresh core --${NEW_CHANNEL}"
|
|
remote.exec "snap info core" | grep -E "^tracking: +latest/${NEW_CHANNEL}"
|
|
fi
|
|
|
|
if nested_is_core_ge 18; then
|
|
remote.exec "sudo snap refresh snapd --${NEW_CHANNEL}"
|
|
remote.exec "snap info snapd" | grep -E "^tracking: +latest/${NEW_CHANNEL}"
|
|
else
|
|
CHANGE_ID=$(remote.exec "sudo snap refresh core --${NEW_CHANNEL} --no-wait")
|
|
nested_wait_for_no_ssh 200 1
|
|
nested_wait_for_ssh 300 1
|
|
# wait for the refresh to be done before checking, if we check too
|
|
# quickly then operations on the core snap like reverting, etc. may
|
|
# fail because it will have refresh-snap change in progress
|
|
remote.exec "snap watch $CHANGE_ID"
|
|
remote.exec "snap info core" | grep -E "^tracking: +latest/${NEW_CHANNEL}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_get_snakeoil_key() {
|
|
local KEYNAME="PkKek-1-snakeoil"
|
|
local VERSION
|
|
VERSION="$(nested_get_version)"
|
|
wget -q https://raw.githubusercontent.com/snapcore/pc-amd64-gadget/"$VERSION"/snakeoil/"$KEYNAME".key
|
|
wget -q https://raw.githubusercontent.com/snapcore/pc-amd64-gadget/"$VERSION"/snakeoil/"$KEYNAME".pem
|
|
echo "$KEYNAME"
|
|
}
|
|
|
|
nested_secboot_remove_signature() {
|
|
local FILE="$1"
|
|
while sbverify --list "$FILE" | grep "^signature [0-9]*$"; do
|
|
sbattach --remove "$FILE"
|
|
done
|
|
}
|
|
|
|
nested_secboot_sign_file() {
|
|
local FILE="$1"
|
|
local KEY="$2"
|
|
local CERT="$3"
|
|
nested_secboot_remove_signature "$FILE"
|
|
sbsign --key "$KEY" --cert "$CERT" --output "$FILE" "$FILE"
|
|
}
|
|
|
|
nested_secboot_sign_gadget() {
|
|
local GADGET_DIR="$1"
|
|
local KEY="$2"
|
|
local CERT="$3"
|
|
if [ -f "$GADGET_DIR/fb.efi" ]; then
|
|
nested_secboot_sign_file "$GADGET_DIR/fb.efi" "$KEY" "$CERT"
|
|
fi
|
|
nested_secboot_sign_file "$GADGET_DIR/shim.efi.signed" "$KEY" "$CERT"
|
|
}
|
|
|
|
nested_secboot_sign_kernel() {
|
|
local KERNEL_DIR="$1"
|
|
local KEY="$2"
|
|
local CERT="$3"
|
|
nested_secboot_sign_file "$KERNEL_DIR/kernel.efi" "$KEY" "$CERT"
|
|
}
|
|
|
|
nested_prepare_env() {
|
|
mkdir -p "$NESTED_IMAGES_DIR"
|
|
mkdir -p "$NESTED_RUNTIME_DIR"
|
|
mkdir -p "$NESTED_ASSETS_DIR"
|
|
mkdir -p "$NESTED_LOGS_DIR"
|
|
mkdir -p "$(nested_get_extra_snaps_path)"
|
|
}
|
|
|
|
nested_cleanup_env() {
|
|
rm -rf "$NESTED_RUNTIME_DIR"
|
|
rm -rf "$NESTED_ASSETS_DIR"
|
|
rm -rf "$NESTED_LOGS_DIR"
|
|
rm -rf "$NESTED_IMAGES_DIR"/*.img
|
|
rm -rf "$(nested_get_extra_snaps_path)"
|
|
}
|
|
|
|
nested_get_image_name() {
|
|
local TYPE="$1"
|
|
local SOURCE="${NESTED_CORE_CHANNEL}"
|
|
local NAME="${NESTED_IMAGE_ID:-generic}"
|
|
local VERSION
|
|
|
|
VERSION="$(nested_get_version)"
|
|
# Use task name to build the image in case the NESTED_IMAGE_ID is unset
|
|
# This scenario is valid on manual tests when it is required to set the NESTED_IMAGE_ID
|
|
if [ "$NAME" = "unset" ]; then
|
|
NAME="$(basename "$SPREAD_TASK")"
|
|
if [ -n "$SPREAD_VARIANT" ]; then
|
|
NAME="${NAME}_${SPREAD_VARIANT}"
|
|
fi
|
|
fi
|
|
|
|
if [ "$NESTED_BUILD_SNAPD_FROM_CURRENT" = "true" ]; then
|
|
SOURCE="custom"
|
|
fi
|
|
if [ "$(nested_get_extra_snaps | wc -l)" != "0" ]; then
|
|
SOURCE="custom"
|
|
fi
|
|
echo "ubuntu-${TYPE}-${VERSION}-${SOURCE}-${NAME}.img"
|
|
}
|
|
|
|
nested_is_generic_image() {
|
|
test -z "${NESTED_IMAGE_ID:-}"
|
|
}
|
|
|
|
nested_get_extra_snaps_path() {
|
|
echo "/tmp/extra-snaps"
|
|
}
|
|
|
|
nested_get_assets_path() {
|
|
echo "$NESTED_ASSETS_DIR"
|
|
}
|
|
|
|
nested_get_images_path() {
|
|
echo "$NESTED_IMAGES_DIR"
|
|
}
|
|
|
|
nested_get_extra_snaps() {
|
|
local EXTRA_SNAPS=""
|
|
local EXTRA_SNAPS_PATH
|
|
EXTRA_SNAPS_PATH="$(nested_get_extra_snaps_path)"
|
|
|
|
if [ -d "$EXTRA_SNAPS_PATH" ]; then
|
|
while IFS= read -r mysnap; do
|
|
echo "$mysnap"
|
|
done < <(find "$EXTRA_SNAPS_PATH" -name '*.snap')
|
|
fi
|
|
}
|
|
|
|
nested_download_image() {
|
|
local IMAGE_URL=$1
|
|
local IMAGE_NAME=$2
|
|
|
|
curl -C - -L -o "${NESTED_IMAGES_DIR}/${IMAGE_NAME}" "$IMAGE_URL"
|
|
|
|
if [[ "$IMAGE_URL" == *.img.xz ]]; then
|
|
mv "${NESTED_IMAGES_DIR}/${IMAGE_NAME}" "${NESTED_IMAGES_DIR}/${IMAGE_NAME}.xz"
|
|
unxz "${NESTED_IMAGES_DIR}/${IMAGE_NAME}.xz"
|
|
elif [[ "$IMAGE_URL" == *.img ]]; then
|
|
echo "Image doesn't need to be decompressed"
|
|
else
|
|
echo "Image extension not supported for image $IMAGE_URL, exiting..."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
nested_get_version() {
|
|
if nested_is_core_16_system; then
|
|
echo "16"
|
|
elif nested_is_core_18_system; then
|
|
echo "18"
|
|
elif nested_is_core_20_system; then
|
|
echo "20"
|
|
elif nested_is_core_22_system; then
|
|
echo "22"
|
|
elif nested_is_core_24_system; then
|
|
echo "24"
|
|
fi
|
|
}
|
|
|
|
nested_get_model() {
|
|
# use custom model if defined
|
|
if [ -n "$NESTED_CUSTOM_MODEL" ]; then
|
|
VERSION="$(nested_get_version)"
|
|
# shellcheck disable=SC2001
|
|
echo "$NESTED_CUSTOM_MODEL" | sed "s/{VERSION}/$VERSION/g"
|
|
return
|
|
fi
|
|
case "$SPREAD_SYSTEM" in
|
|
ubuntu-16.04-64)
|
|
echo "$TESTSLIB/assertions/nested-amd64.model"
|
|
;;
|
|
ubuntu-18.04-64)
|
|
echo "$TESTSLIB/assertions/nested-18-amd64.model"
|
|
;;
|
|
ubuntu-20.04-64)
|
|
echo "$TESTSLIB/assertions/nested-20-amd64.model"
|
|
;;
|
|
ubuntu-22.04-64)
|
|
echo "$TESTSLIB/assertions/nested-22-amd64.model"
|
|
;;
|
|
ubuntu-22.04-arm-64)
|
|
echo "$TESTSLIB/assertions/nested-22-arm64.model"
|
|
;;
|
|
ubuntu-24.04-64)
|
|
echo "$TESTSLIB/assertions/nested-24-amd64.model"
|
|
;;
|
|
*)
|
|
echo "unsupported system"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
nested_model_authority() {
|
|
local model
|
|
model="$(nested_get_model)"
|
|
grep "authority-id:" "$model"|cut -d ' ' -f2
|
|
}
|
|
|
|
nested_ensure_ubuntu_save() {
|
|
local GADGET_DIR="$1"
|
|
shift
|
|
"$TESTSLIB"/ensure_ubuntu_save.py "$@" "$GADGET_DIR"/meta/gadget.yaml > /tmp/gadget-with-save.yaml
|
|
if [ "$(cat /tmp/gadget-with-save.yaml)" != "" ]; then
|
|
mv /tmp/gadget-with-save.yaml "$GADGET_DIR"/meta/gadget.yaml
|
|
else
|
|
rm -f /tmp/gadget-with-save.yaml
|
|
fi
|
|
}
|
|
|
|
nested_prepare_snapd() {
|
|
if [ "$NESTED_BUILD_SNAPD_FROM_CURRENT" = "true" ]; then
|
|
echo "Repacking snapd snap"
|
|
local snap_name output_name snap_id
|
|
if nested_is_core_16_system; then
|
|
if [ ! -f "$NESTED_ASSETS_DIR/core-from-snapd-deb.snap" ]; then
|
|
"$TESTSTOOLS"/snaps-state repack_snapd_deb_into_snap core "$NESTED_ASSETS_DIR"
|
|
cp "$NESTED_ASSETS_DIR/core-from-snapd-deb.snap" "$(nested_get_extra_snaps_path)/core-from-snapd-deb.snap"
|
|
fi
|
|
# sign the snapd snap with fakestore if requested
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack "$NESTED_FAKESTORE_BLOB_DIR" "$(nested_get_extra_snaps_path)/core-from-snapd-deb.snap" "99T7MUlRhtI3U0QFgl5mXXESAiSwt776"
|
|
fi
|
|
else
|
|
for f in "${NESTED_ASSETS_DIR}"/snapd_*.snap; do
|
|
snap_name="$(basename "${f}")"
|
|
break
|
|
done
|
|
if [ ! -f "${NESTED_ASSETS_DIR}/${snap_name}" ]; then
|
|
# shellcheck source=tests/lib/prepare.sh
|
|
. "$TESTSLIB"/prepare.sh
|
|
build_snapd_snap "$NESTED_ASSETS_DIR"
|
|
for f in "${NESTED_ASSETS_DIR}"/snapd_*.snap; do
|
|
snap_name="$(basename "${f}")"
|
|
break
|
|
done
|
|
cp "${NESTED_ASSETS_DIR}/${snap_name}" "$(nested_get_extra_snaps_path)/"
|
|
fi
|
|
# sign the snapd snap with fakestore if requested
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack "$NESTED_FAKESTORE_BLOB_DIR" "$(nested_get_extra_snaps_path)/${snap_name}" "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_prepare_kernel() {
|
|
# allow repacking the kernel
|
|
if [ "$NESTED_REPACK_KERNEL_SNAP" = "true" ]; then
|
|
echo "Repacking kernel snap"
|
|
local kernel_snap output_name snap_id version
|
|
output_name="pc-kernel.snap"
|
|
snap_id="pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza"
|
|
version="$(nested_get_version)"
|
|
|
|
if [ ! -f "$NESTED_ASSETS_DIR/$output_name" ]; then
|
|
if nested_is_core_le 18; then
|
|
kernel_snap=pc-kernel-new.snap
|
|
repack_kernel_snap "$kernel_snap"
|
|
|
|
elif nested_is_core_ge 20; then
|
|
snap download --basename=pc-kernel --channel="$version/${NESTED_KERNEL_CHANNEL}" pc-kernel
|
|
|
|
# set the unix bump time if the NESTED_* var is set,
|
|
# otherwise leave it empty
|
|
local epochBumpTime
|
|
epochBumpTime=${NESTED_CORE20_INITRAMFS_EPOCH_TIMESTAMP:-}
|
|
if [ -n "$epochBumpTime" ]; then
|
|
epochBumpTime="--epoch-bump-time=$epochBumpTime"
|
|
fi
|
|
|
|
if nested_is_core_24_system; then
|
|
uc24_build_initramfs_kernel_snap "pc-kernel.snap" "$NESTED_ASSETS_DIR" "$epochBumpTime"
|
|
else
|
|
uc20_build_initramfs_kernel_snap "pc-kernel.snap" "$NESTED_ASSETS_DIR" "$epochBumpTime"
|
|
fi
|
|
rm -f "pc-kernel.snap" "pc-kernel.assert"
|
|
|
|
# Prepare the pc kernel snap
|
|
kernel_snap=$(ls "$NESTED_ASSETS_DIR"/pc-kernel_*.snap)
|
|
chmod 0600 "$kernel_snap"
|
|
fi
|
|
mv "$kernel_snap" "$NESTED_ASSETS_DIR/$output_name"
|
|
fi
|
|
cp "$NESTED_ASSETS_DIR/$output_name" "$(nested_get_extra_snaps_path)/$output_name"
|
|
|
|
# sign the pc-kernel snap with fakestore if requested
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack "$NESTED_FAKESTORE_BLOB_DIR" "$(nested_get_extra_snaps_path)/$output_name" "$snap_id"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_prepare_gadget() {
|
|
if [ "$NESTED_REPACK_GADGET_SNAP" = "true" ]; then
|
|
if nested_is_core_ge 20; then
|
|
# Prepare the pc gadget snap (unless provided by extra-snaps)
|
|
local snap_id version gadget_snap
|
|
version="$(nested_get_version)"
|
|
snap_id="UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH"
|
|
|
|
existing_snap=$(find "$(nested_get_extra_snaps_path)" -name 'pc_*.snap')
|
|
if [ -n "$existing_snap" ]; then
|
|
echo "Using generated pc gadget snap $existing_snap"
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack --extra-decl-json "$NESTED_FAKESTORE_SNAP_DECL_PC_GADGET" "$NESTED_FAKESTORE_BLOB_DIR" "$existing_snap" "$snap_id"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# XXX: deal with [ "$NESTED_ENABLE_SECURE_BOOT" != "true" ] && [ "$NESTED_ENABLE_TPM" != "true" ]
|
|
echo "Repacking pc snap"
|
|
# Get the snakeoil key and cert
|
|
local key_name snakeoil_key snakeoil_cert
|
|
key_name=$(nested_get_snakeoil_key)
|
|
snakeoil_key="$PWD/$key_name.key"
|
|
snakeoil_cert="$PWD/$key_name.pem"
|
|
|
|
snap download --basename=pc --channel="$version/${NESTED_GADGET_CHANNEL}" pc
|
|
unsquashfs -d pc-gadget pc.snap
|
|
nested_secboot_sign_gadget pc-gadget "$snakeoil_key" "$snakeoil_cert"
|
|
case "${NESTED_UBUNTU_SAVE:-}" in
|
|
add)
|
|
# ensure that ubuntu-save is present
|
|
nested_ensure_ubuntu_save pc-gadget --add
|
|
touch ubuntu-save-added
|
|
;;
|
|
remove)
|
|
# ensure that ubuntu-save is removed
|
|
nested_ensure_ubuntu_save pc-gadget --remove
|
|
touch ubuntu-save-removed
|
|
;;
|
|
esac
|
|
|
|
# also make logging persistent for easier debugging of
|
|
# test failures, otherwise we have no way to see what
|
|
# happened during a failed nested VM boot where we
|
|
# weren't able to login to a device
|
|
cat >> pc-gadget/meta/gadget.yaml << EOF
|
|
defaults:
|
|
system:
|
|
journal:
|
|
persistent: true
|
|
EOF
|
|
local GADGET_EXTRA_CMDLINE=""
|
|
if [ "$NESTED_SNAPD_DEBUG_TO_SERIAL" = "true" ]; then
|
|
# add snapd debug and log to serial console for extra
|
|
# visibility what happens when a machine fails to boot
|
|
GADGET_EXTRA_CMDLINE="console=ttyS0 snapd.debug=1 systemd.journald.forward_to_console=1"
|
|
fi
|
|
if [ -n "$NESTED_EXTRA_CMDLINE" ]; then
|
|
GADGET_EXTRA_CMDLINE="$GADGET_EXTRA_CMDLINE $NESTED_EXTRA_CMDLINE"
|
|
fi
|
|
|
|
if [ -n "$GADGET_EXTRA_CMDLINE" ]; then
|
|
echo "Configuring command line parameters in the gadget snap: \"console=ttyS0 $GADGET_EXTRA_CMDLINE\""
|
|
echo "$GADGET_EXTRA_CMDLINE" > pc-gadget/cmdline.extra
|
|
fi
|
|
|
|
# pack the gadget
|
|
snap pack pc-gadget/ "$NESTED_ASSETS_DIR"
|
|
|
|
gadget_snap=$(ls "$NESTED_ASSETS_DIR"/pc_*.snap)
|
|
cp "$gadget_snap" "$(nested_get_extra_snaps_path)/pc.snap"
|
|
rm -f "pc.snap" "pc.assert" "$snakeoil_key" "$snakeoil_cert"
|
|
fi
|
|
# sign the pc gadget snap with fakestore if requested
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
# XXX: this is a bit of a hack, but some nested tests
|
|
# need extra bits in their snap declaration, so inject
|
|
# that here, it could end up being empty in which case
|
|
# it is ignored
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack --extra-decl-json "$NESTED_FAKESTORE_SNAP_DECL_PC_GADGET" "$NESTED_FAKESTORE_BLOB_DIR" "$(nested_get_extra_snaps_path)/pc.snap" "$snap_id"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_prepare_base() {
|
|
if [ "$NESTED_REPACK_BASE_SNAP" = "true" ]; then
|
|
if nested_is_core_16_system; then
|
|
echo "No base snap to prepare in core 16"
|
|
return
|
|
elif nested_is_core_18_system; then
|
|
snap_name="core18"
|
|
snap_id="CSO04Jhav2yK0uz97cr0ipQRyqg0qQL6"
|
|
elif nested_is_core_20_system; then
|
|
snap_name="core20"
|
|
snap_id="DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q"
|
|
elif nested_is_core_22_system; then
|
|
snap_name="core22"
|
|
snap_id="amcUKQILKXHHTlmSa7NMdnXSx02dNeeT"
|
|
elif nested_is_core_24_system; then
|
|
snap_name="core24"
|
|
snap_id="dwTAh7MZZ01zyriOZErqd1JynQLiOGvM"
|
|
fi
|
|
output_name="${snap_name}.snap"
|
|
|
|
existing_snap=$(find "$(nested_get_extra_snaps_path)" -name "${snap_name}*.snap")
|
|
if [ -n "$existing_snap" ]; then
|
|
echo "Using generated base snap $existing_snap"
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack "$NESTED_FAKESTORE_BLOB_DIR" "$existing_snap" "$snap_id"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
if [ ! -f "$NESTED_ASSETS_DIR/$output_name" ]; then
|
|
echo "Repacking $snap_name snap"
|
|
snap download --channel="$CORE_CHANNEL" --basename="$snap_name" "$snap_name"
|
|
repack_core_snap_with_tweaks "${snap_name}.snap" "new-${snap_name}.snap"
|
|
rm -f "$snap_name".snap "$snap_name".assert
|
|
|
|
mv "new-${snap_name}.snap" "$NESTED_ASSETS_DIR/$output_name"
|
|
fi
|
|
cp "$NESTED_ASSETS_DIR/$output_name" "$(nested_get_extra_snaps_path)/$output_name"
|
|
|
|
# sign the base snap with fakestore if requested
|
|
if [ "$NESTED_SIGN_SNAPS_FAKESTORE" = "true" ]; then
|
|
"$TESTSTOOLS"/store-state make-snap-installable --noack "$NESTED_FAKESTORE_BLOB_DIR" "$(nested_get_extra_snaps_path)/${snap_name}.snap" "$snap_id"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_prepare_essential_snaps() {
|
|
# shellcheck source=tests/lib/prepare.sh
|
|
. "$TESTSLIB"/prepare.sh
|
|
# shellcheck source=tests/lib/snaps.sh
|
|
. "$TESTSLIB"/snaps.sh
|
|
|
|
nested_prepare_snapd
|
|
nested_prepare_kernel
|
|
nested_prepare_gadget
|
|
nested_prepare_base
|
|
}
|
|
|
|
nested_configure_default_user() {
|
|
local IMAGE_NAME
|
|
IMAGE_NAME="$(nested_get_image_name core)"
|
|
# Configure the user for the vm
|
|
if [ "$NESTED_USE_CLOUD_INIT" = "true" ]; then
|
|
if nested_is_core_ge 20; then
|
|
nested_configure_cloud_init_on_core20_vm "$NESTED_IMAGES_DIR/$IMAGE_NAME"
|
|
else
|
|
nested_configure_cloud_init_on_core_vm "$NESTED_IMAGES_DIR/$IMAGE_NAME"
|
|
fi
|
|
else
|
|
nested_create_assertions_disk
|
|
fi
|
|
|
|
# Save a copy of the image
|
|
cp -v "$NESTED_IMAGES_DIR/$IMAGE_NAME" "$NESTED_IMAGES_DIR/$IMAGE_NAME.pristine"
|
|
}
|
|
|
|
nested_create_core_vm() {
|
|
# shellcheck source=tests/lib/prepare.sh
|
|
. "$TESTSLIB"/prepare.sh
|
|
# shellcheck source=tests/lib/snaps.sh
|
|
. "$TESTSLIB"/snaps.sh
|
|
|
|
local IMAGE_NAME
|
|
IMAGE_NAME="$(nested_get_image_name core)"
|
|
mkdir -p "$NESTED_IMAGES_DIR"
|
|
|
|
if [ -f "$NESTED_IMAGES_DIR/$IMAGE_NAME.pristine" ]; then
|
|
cp -v "$NESTED_IMAGES_DIR/$IMAGE_NAME.pristine" "$NESTED_IMAGES_DIR/$IMAGE_NAME"
|
|
if [ ! "$NESTED_USE_CLOUD_INIT" = "true" ]; then
|
|
nested_create_assertions_disk
|
|
fi
|
|
return
|
|
|
|
elif [ ! -f "$NESTED_IMAGES_DIR/$IMAGE_NAME" ]; then
|
|
if [ -n "$NESTED_CUSTOM_IMAGE_URL" ]; then
|
|
# download the ubuntu-core image from $CUSTOM_IMAGE_URL
|
|
nested_download_image "$NESTED_CUSTOM_IMAGE_URL" "$IMAGE_NAME"
|
|
else
|
|
# create the ubuntu-core image
|
|
local UBUNTU_IMAGE="$GOHOME"/bin/ubuntu-image
|
|
if os.query is-xenial || os.query is-arm; then
|
|
# ubuntu-image on 16.04 needs to be installed from a snap
|
|
UBUNTU_IMAGE=/snap/bin/ubuntu-image
|
|
fi
|
|
|
|
if [ "$NESTED_BUILD_SNAPD_FROM_CURRENT" = "true" ]; then
|
|
nested_prepare_snapd
|
|
nested_prepare_kernel
|
|
nested_prepare_gadget
|
|
nested_prepare_base
|
|
fi
|
|
|
|
# Invoke ubuntu image
|
|
local NESTED_MODEL
|
|
NESTED_MODEL="$(nested_get_model)"
|
|
|
|
local EXTRA_SNAPS=""
|
|
for mysnap in $(nested_get_extra_snaps); do
|
|
EXTRA_SNAPS="$EXTRA_SNAPS --snap $mysnap"
|
|
done
|
|
|
|
# only set SNAPPY_FORCE_SAS_URL because we don't need it defined
|
|
# anywhere else but here, where snap prepare-image as called by
|
|
# ubuntu-image will look for assertions for the snaps we provide
|
|
# to it
|
|
SNAPPY_FORCE_SAS_URL="$NESTED_UBUNTU_IMAGE_SNAPPY_FORCE_SAS_URL"
|
|
export SNAPPY_FORCE_SAS_URL
|
|
UBUNTU_IMAGE_SNAP_CMD=/usr/bin/snap
|
|
export UBUNTU_IMAGE_SNAP_CMD
|
|
if [ -n "$NESTED_CORE_CHANNEL" ]; then
|
|
UBUNTU_IMAGE_CHANNEL_ARG="--channel $NESTED_CORE_CHANNEL"
|
|
else
|
|
UBUNTU_IMAGE_CHANNEL_ARG=""
|
|
fi
|
|
|
|
declare -a UBUNTU_IMAGE_PRESEED_ARGS
|
|
if [ -n "$NESTED_UBUNTU_IMAGE_PRESEED_KEY" ]; then
|
|
# shellcheck disable=SC2191
|
|
UBUNTU_IMAGE_PRESEED_ARGS+=(--preseed --preseed-sign-key=\""$NESTED_UBUNTU_IMAGE_PRESEED_KEY"\")
|
|
fi
|
|
# ubuntu-image creates sparse image files
|
|
# shellcheck disable=SC2086
|
|
SNAPD_DEBUG=1 "$UBUNTU_IMAGE" snap --image-size 10G --validation=enforce \
|
|
"$NESTED_MODEL" \
|
|
$UBUNTU_IMAGE_CHANNEL_ARG \
|
|
"${UBUNTU_IMAGE_PRESEED_ARGS[@]:-}" \
|
|
--output-dir "$NESTED_IMAGES_DIR" \
|
|
--sector-size "${NESTED_DISK_LOGICAL_BLOCK_SIZE}" \
|
|
$EXTRA_SNAPS |& tee "$NESTED_LOGS_DIR/ubuntu-image.log"
|
|
|
|
# ubuntu-image dropped the --output parameter, so we have to rename
|
|
# the image ourselves, the images are named after volumes listed in
|
|
# gadget.yaml
|
|
find "$NESTED_IMAGES_DIR/" -maxdepth 1 -name '*.img' | while read -r imgname; do
|
|
if [ -e "$NESTED_IMAGES_DIR/$IMAGE_NAME" ]; then
|
|
echo "Image $IMAGE_NAME file already present"
|
|
exit 1
|
|
fi
|
|
mv "$imgname" "$NESTED_IMAGES_DIR/$IMAGE_NAME"
|
|
done
|
|
unset SNAPPY_FORCE_SAS_URL
|
|
unset UBUNTU_IMAGE_SNAP_CMD
|
|
fi
|
|
fi
|
|
|
|
nested_configure_default_user
|
|
}
|
|
|
|
nested_configure_cloud_init_on_core_vm() {
|
|
local IMAGE=$1
|
|
nested_create_cloud_init_data "$NESTED_ASSETS_DIR/user-data" "$NESTED_ASSETS_DIR/meta-data"
|
|
|
|
local devloop writableDev tmp
|
|
# mount the image and find the loop device /dev/loop that is created for it
|
|
kpartx -avs "$IMAGE"
|
|
devloop=$(losetup --list --noheadings | grep "$IMAGE" | awk '{print $1}')
|
|
dev=$(basename "$devloop")
|
|
|
|
# we add cloud-init data to the 3rd partition, which is writable
|
|
writableDev="/dev/mapper/${dev}p3"
|
|
|
|
# wait for the loop device to show up
|
|
retry -n 3 --wait 1 test -e "$writableDev"
|
|
tmp=$(mktemp -d)
|
|
mount "$writableDev" "$tmp"
|
|
|
|
# use nocloud-net for the dir to copy data into
|
|
mkdir -p "$tmp/system-data/var/lib/cloud/seed/nocloud-net/"
|
|
cp "$NESTED_ASSETS_DIR/user-data" "$tmp/system-data/var/lib/cloud/seed/nocloud-net/"
|
|
cp "$NESTED_ASSETS_DIR/meta-data" "$tmp/system-data/var/lib/cloud/seed/nocloud-net/"
|
|
|
|
sync
|
|
umount "$tmp"
|
|
kpartx -d "$IMAGE"
|
|
}
|
|
|
|
nested_create_cloud_init_data() {
|
|
local USER_DATA=$1
|
|
local META_DATA=$2
|
|
cat <<EOF > "$USER_DATA"
|
|
#cloud-config
|
|
ssh_pwauth: True
|
|
users:
|
|
- name: user1
|
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
shell: /bin/bash
|
|
chpasswd:
|
|
list: |
|
|
user1:ubuntu
|
|
expire: False
|
|
EOF
|
|
|
|
cat <<EOF > "$META_DATA"
|
|
instance_id: cloud-images
|
|
EOF
|
|
}
|
|
|
|
# TODO: see if the uc20 config works for classic here too, that would be faster
|
|
# as the chpasswd module from cloud-init runs rather late in the boot
|
|
nested_create_cloud_init_config() {
|
|
local CONFIG_PATH=$1
|
|
cat <<EOF > "$CONFIG_PATH"
|
|
#cloud-config
|
|
ssh_pwauth: True
|
|
users:
|
|
- name: user1
|
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
shell: /bin/bash
|
|
chpasswd:
|
|
list: |
|
|
user1:ubuntu
|
|
expire: False
|
|
datasource_list: [ NoCloud, None ]
|
|
datasource:
|
|
NoCloud:
|
|
userdata_raw: |
|
|
#!/bin/bash
|
|
logger -t nested test running || true
|
|
EOF
|
|
}
|
|
|
|
nested_create_cloud_init_uc20_config() {
|
|
local CONFIG_PATH=$1
|
|
cat << 'EOF' > "$CONFIG_PATH"
|
|
#cloud-config
|
|
datasource_list: [ None ]
|
|
users:
|
|
- name: user1
|
|
sudo: "ALL=(ALL) NOPASSWD:ALL"
|
|
lock_passwd: false
|
|
plain_text_passwd: "ubuntu"
|
|
EOF
|
|
}
|
|
|
|
nested_add_file_to_image() {
|
|
local IMAGE=$1
|
|
local FILE=$2
|
|
local devloop ubuntuSeedDev tmp
|
|
# Bind the image the a free loop device
|
|
devloop="$(retry -n 3 --wait 1 losetup -f --show -P --sector-size "${NESTED_DISK_LOGICAL_BLOCK_SIZE}" "${IMAGE}")"
|
|
|
|
# we add cloud-init data to the 2nd partition, which is ubuntu-seed
|
|
ubuntuSeedDev="${devloop}p2"
|
|
if os.query is-arm; then
|
|
# In arm the BIOS partition does not exist, so ubuntu-seed is the p1
|
|
ubuntuSeedDev="${devloop}p1"
|
|
fi
|
|
|
|
# Wait for the partition to show up
|
|
retry -n 2 --wait 1 test -b "${ubuntuSeedDev}" || true
|
|
|
|
# losetup does not set the right block size on LOOP_CONFIGURE
|
|
# but with LOOP_SET_BLOCK_SIZE later. So the block size might have
|
|
# been wrong during the part scan. In this case we need to rescan
|
|
# manually.
|
|
if ! [ -b "${ubuntuSeedDev}" ]; then
|
|
partx -u "${devloop}"
|
|
# Wait for the partition to show up
|
|
retry -n 2 --wait 1 test -b "${ubuntuSeedDev}"
|
|
fi
|
|
|
|
tmp=$(mktemp -d)
|
|
retry -n 5 --wait 2 mount "$ubuntuSeedDev" "$tmp"
|
|
mkdir -p "$tmp/data/etc/cloud/cloud.cfg.d/"
|
|
cp -f "$FILE" "$tmp/data/etc/cloud/cloud.cfg.d/"
|
|
sync
|
|
umount "$tmp"
|
|
losetup -d "${devloop}"
|
|
}
|
|
|
|
nested_configure_cloud_init_on_core20_vm() {
|
|
local IMAGE=$1
|
|
nested_create_cloud_init_uc20_config "$NESTED_ASSETS_DIR/data.cfg"
|
|
|
|
nested_add_file_to_image "$IMAGE" "$NESTED_ASSETS_DIR/data.cfg"
|
|
}
|
|
|
|
nested_save_serial_log() {
|
|
if [ -f "${NESTED_LOGS_DIR}/serial.log" ]; then
|
|
for i in $(seq 1 9); do
|
|
if [ ! -f "${NESTED_LOGS_DIR}/serial.log.${i}" ]; then
|
|
cp "${NESTED_LOGS_DIR}/serial.log" "${NESTED_LOGS_DIR}/serial.log.${i}"
|
|
break
|
|
fi
|
|
done
|
|
# make sure we start with clean log file
|
|
echo > "${NESTED_LOGS_DIR}/serial.log"
|
|
fi
|
|
}
|
|
|
|
nested_print_serial_log() {
|
|
if [ -f "${NESTED_LOGS_DIR}/serial.log.1" ]; then
|
|
# here we disable SC2045 because previously it is checked there is at least
|
|
# 1 file which matches. In this case ls command is needed because it is important
|
|
# to get the list in reverse order.
|
|
# shellcheck disable=SC2045
|
|
for logfile in $(ls "${NESTED_LOGS_DIR}"/serial.log.*); do
|
|
cat "$logfile"
|
|
done
|
|
fi
|
|
if [ -f "${NESTED_LOGS_DIR}/serial.log" ]; then
|
|
cat "${NESTED_LOGS_DIR}/serial.log"
|
|
fi
|
|
}
|
|
|
|
nested_force_stop_vm() {
|
|
systemctl stop "$NESTED_VM"
|
|
}
|
|
|
|
nested_force_start_vm() {
|
|
# if the $NESTED_VM is using a swtpm, we need to wait until the file exists
|
|
# because the file disappears temporarily after qemu exits
|
|
if systemctl show "$NESTED_VM" -p ExecStart | grep -q test-snapd-swtpm; then
|
|
retry -n 10 --wait 1 test -S /var/snap/test-snapd-swtpm/current/swtpm-sock
|
|
fi
|
|
systemctl start "$NESTED_VM"
|
|
}
|
|
|
|
nested_start_core_vm_unit() {
|
|
local QEMU CURRENT_IMAGE
|
|
CURRENT_IMAGE=$1
|
|
QEMU=$(nested_qemu_name)
|
|
|
|
# Now qemu parameters are defined
|
|
|
|
# use only 2G of RAM for qemu-nested
|
|
# the caller can override PARAM_MEM
|
|
local PARAM_MEM PARAM_SMP
|
|
if [ "$SPREAD_BACKEND" = "google-nested" ] || [ "$SPREAD_BACKEND" = "google-nested-arm" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-4096}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-2}"
|
|
elif [ "$SPREAD_BACKEND" = "google-nested-dev" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-8192}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-4}"
|
|
elif [ "$SPREAD_BACKEND" = "qemu-nested" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-2048}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-1}"
|
|
else
|
|
echo "unknown spread backend $SPREAD_BACKEND"
|
|
exit 1
|
|
fi
|
|
|
|
PARAM_PHYS_BLOCK_SIZE="physical_block_size=${NESTED_DISK_PHYSICAL_BLOCK_SIZE}"
|
|
PARAM_LOGI_BLOCK_SIZE="logical_block_size=${NESTED_DISK_LOGICAL_BLOCK_SIZE}"
|
|
|
|
local PARAM_DISPLAY PARAM_NETWORK PARAM_MONITOR PARAM_USB PARAM_CD PARAM_RANDOM PARAM_CPU PARAM_TRACE PARAM_LOG PARAM_SERIAL PARAM_RTC
|
|
PARAM_DISPLAY="-nographic"
|
|
PARAM_NETWORK="-net nic,model=virtio -net user,hostfwd=tcp::$NESTED_SSH_PORT-:22,hostfwd=tcp::8023-:8023,hostfwd=tcp::9022-:9022"
|
|
PARAM_MONITOR="-monitor tcp:127.0.0.1:$NESTED_MON_PORT,server=on,wait=off"
|
|
PARAM_USB="-usb"
|
|
PARAM_CD="${NESTED_PARAM_CD:-}"
|
|
PARAM_RANDOM="-object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0"
|
|
PARAM_CPU=""
|
|
PARAM_TRACE="-d cpu_reset"
|
|
PARAM_LOG="-D $NESTED_LOGS_DIR/qemu.log"
|
|
PARAM_RTC="${NESTED_PARAM_RTC:-}"
|
|
PARAM_EXTRA="${NESTED_PARAM_EXTRA:-}"
|
|
|
|
# Open port 7777 on the host so that failures in the nested VM (e.g. to
|
|
# create users) can be debugged interactively via
|
|
# "telnet localhost 7777". Also keeps the logs
|
|
#
|
|
# XXX: should serial just be logged to stdout so that we just need
|
|
# to "journalctl -u $NESTED_VM" to see what is going on ?
|
|
if "$QEMU" -version | grep '2\.5'; then
|
|
# XXX: remove once we no longer support xenial hosts
|
|
PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log"
|
|
else
|
|
PARAM_SERIAL="-chardev socket,telnet=on,host=localhost,server=on,port=7777,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0"
|
|
fi
|
|
|
|
# save logs from previous runs
|
|
nested_save_serial_log
|
|
|
|
# Set kvm attribute
|
|
local ATTR_KVM
|
|
ATTR_KVM=""
|
|
if nested_is_kvm_enabled; then
|
|
ATTR_KVM=",accel=kvm"
|
|
# CPU can be defined just when kvm is enabled
|
|
PARAM_CPU="-cpu host"
|
|
fi
|
|
|
|
local PARAM_MACHINE
|
|
if [[ "$SPREAD_BACKEND" = google-nested* ]]; then
|
|
if os.query is-arm; then
|
|
PARAM_MACHINE="-machine virt${ATTR_KVM}"
|
|
PARAM_CPU="-cpu host"
|
|
else
|
|
PARAM_MACHINE="-machine ubuntu${ATTR_KVM}"
|
|
fi
|
|
elif [ "$SPREAD_BACKEND" = "qemu-nested" ]; then
|
|
# check if we have nested kvm
|
|
if [ "$(cat /sys/module/kvm_*/parameters/nested)" = "1" ]; then
|
|
PARAM_MACHINE="-machine ubuntu${ATTR_KVM}"
|
|
else
|
|
# and if not reset kvm related parameters
|
|
PARAM_MACHINE=""
|
|
PARAM_CPU=""
|
|
ATTR_KVM=""
|
|
fi
|
|
else
|
|
echo "unknown spread backend $SPREAD_BACKEND"
|
|
exit 1
|
|
fi
|
|
|
|
local PARAM_ASSERTIONS PARAM_BIOS PARAM_TPM PARAM_IMAGE
|
|
PARAM_ASSERTIONS=""
|
|
PARAM_BIOS=""
|
|
PARAM_TPM=""
|
|
PARAM_REEXEC_ON_FAILURE=""
|
|
if [ "$NESTED_USE_CLOUD_INIT" != "true" ]; then
|
|
# TODO: fix using the old way of an ext4 formatted drive w/o partitions
|
|
# as this used to work but has since regressed
|
|
|
|
# this simulates a usb drive attached to the device, the removable=true
|
|
# is necessary otherwise snapd will not import it, as snapd only
|
|
# considers removable devices for cold-plug first-boot runs
|
|
# the nec-usb-xhci device is necessary to create the bus we attach the
|
|
# storage to
|
|
PARAM_ASSERTIONS="-drive if=none,id=stick,format=raw,file=$NESTED_ASSETS_DIR/assertions.disk,cache=none,format=raw -device nec-usb-xhci,id=xhci -device usb-storage,bus=xhci.0,removable=true,drive=stick"
|
|
fi
|
|
if nested_is_core_ge 20; then
|
|
# use a bundle EFI bios by default
|
|
if os.query is-arm; then
|
|
PARAM_BIOS="-bios /usr/share/AAVMF/AAVMF_CODE.fd"
|
|
else
|
|
PARAM_BIOS="-bios /usr/share/ovmf/OVMF.fd"
|
|
fi
|
|
local OVMF_CODE OVMF_VARS
|
|
OVMF_CODE=""
|
|
OVMF_VARS=""
|
|
|
|
if nested_is_core_ge 22; then
|
|
wget -q https://storage.googleapis.com/snapd-spread-tests/dependencies/OVMF_CODE.secboot.fd
|
|
mv OVMF_CODE.secboot.fd /usr/share/OVMF/OVMF_CODE.secboot.fd
|
|
wget -q https://storage.googleapis.com/snapd-spread-tests/dependencies/OVMF_VARS.snakeoil.fd
|
|
mv OVMF_VARS.snakeoil.fd /usr/share/OVMF/OVMF_VARS.snakeoil.fd
|
|
wget -q https://storage.googleapis.com/snapd-spread-tests/dependencies/OVMF_VARS.ms.fd
|
|
mv OVMF_VARS.ms.fd /usr/share/OVMF/OVMF_VARS.ms.fd
|
|
fi
|
|
|
|
# In this case the kernel.efi is unsigned and signed with snaleoil certs
|
|
if [ "$NESTED_FORCE_MS_KEYS" != "true" ] && [ "$NESTED_BUILD_SNAPD_FROM_CURRENT" = "true" ]; then
|
|
OVMF_VARS="snakeoil"
|
|
else
|
|
OVMF_VARS="ms"
|
|
fi
|
|
|
|
if nested_is_secure_boot_enabled; then
|
|
OVMF_CODE="secboot"
|
|
if os.query is-arm; then
|
|
cp -f "/usr/share/AAVMF/AAVMF_VARS.fd" "$NESTED_ASSETS_DIR/AAVMF_VARS.fd"
|
|
PARAM_BIOS="-drive file=/usr/share/AAVMF/AAVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on -drive file=$NESTED_ASSETS_DIR/AAVMF_VARS.fd,if=pflash,format=raw"
|
|
else
|
|
cp -f "/usr/share/OVMF/OVMF_VARS.${OVMF_VARS}.fd" "$NESTED_ASSETS_DIR/OVMF_VARS.${OVMF_VARS}.fd"
|
|
PARAM_BIOS="-drive file=/usr/share/OVMF/OVMF_CODE.${OVMF_CODE}.fd,if=pflash,format=raw,unit=0,readonly=on -drive file=$NESTED_ASSETS_DIR/OVMF_VARS.${OVMF_VARS}.fd,if=pflash,format=raw"
|
|
PARAM_MACHINE="-machine q35${ATTR_KVM} -global ICH9-LPC.disable_s3=1"
|
|
fi
|
|
fi
|
|
|
|
if nested_is_tpm_enabled; then
|
|
if snap list test-snapd-swtpm >/dev/null; then
|
|
if [ -z "$NESTED_TPM_NO_RESTART" ]; then
|
|
# reset the tpm state
|
|
snap stop test-snapd-swtpm > /dev/null
|
|
rm /var/snap/test-snapd-swtpm/current/tpm2-00.permall || true
|
|
snap start test-snapd-swtpm > /dev/null
|
|
fi
|
|
else
|
|
snap install test-snapd-swtpm --edge
|
|
fi
|
|
# wait for the tpm sock file to exist
|
|
retry -n 10 --wait 1 test -S /var/snap/test-snapd-swtpm/current/swtpm-sock
|
|
PARAM_TPM="-chardev socket,id=chrtpm,path=/var/snap/test-snapd-swtpm/current/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm"
|
|
if os.query is-arm; then
|
|
PARAM_TPM="$PARAM_TPM -device tpm-tis-device,tpmdev=tpm0"
|
|
else
|
|
PARAM_TPM="$PARAM_TPM -device tpm-tis,tpmdev=tpm0"
|
|
fi
|
|
fi
|
|
PARAM_IMAGE="-drive file=$CURRENT_IMAGE,cache=none,format=raw,id=disk1,if=none -device virtio-blk-pci,drive=disk1,bootindex=1"
|
|
else
|
|
PARAM_IMAGE="-drive file=$CURRENT_IMAGE,cache=none,format=raw,id=disk1,if=none -device ide-hd,drive=disk1"
|
|
fi
|
|
PARAM_IMAGE="$PARAM_IMAGE,${PARAM_PHYS_BLOCK_SIZE},${PARAM_LOGI_BLOCK_SIZE}"
|
|
|
|
if nested_is_core_20_system; then
|
|
# This is to deal with the following qemu error which occurs using q35 machines in focal
|
|
# Error -> Code=qemu-system-x86_64: /build/qemu-rbeYHu/qemu-4.2/include/hw/core/cpu.h:633: cpu_asidx_from_attrs: Assertion `ret < cpu->num_ases && ret >= 0' failed
|
|
# It is reproducible on an Intel machine without unrestricted mode support, the failure is most likely due to the guest entering an invalid state for Intel VT
|
|
# The workaround is to restart the vm and check that qemu doesn't go into this bad state again
|
|
PARAM_REEXEC_ON_FAILURE="[Service]\nRestart=on-failure\nRestartSec=5s"
|
|
fi
|
|
|
|
# ensure we have a log dir
|
|
mkdir -p "$NESTED_LOGS_DIR"
|
|
# make sure we start with clean log file
|
|
echo > "${NESTED_LOGS_DIR}/serial.log"
|
|
# Systemd unit is created, it is important to respect the qemu parameters order
|
|
tests.systemd create-and-start-unit "$NESTED_VM" "${QEMU} \
|
|
${PARAM_SMP} \
|
|
${PARAM_CPU} \
|
|
${PARAM_MEM} \
|
|
${PARAM_TRACE} \
|
|
${PARAM_LOG} \
|
|
${PARAM_RTC} \
|
|
${PARAM_MACHINE} \
|
|
${PARAM_DISPLAY} \
|
|
${PARAM_NETWORK} \
|
|
${PARAM_BIOS} \
|
|
${PARAM_TPM} \
|
|
${PARAM_RANDOM} \
|
|
${PARAM_IMAGE} \
|
|
${PARAM_ASSERTIONS} \
|
|
${PARAM_SERIAL} \
|
|
${PARAM_MONITOR} \
|
|
${PARAM_USB} \
|
|
${PARAM_CD} \
|
|
${PARAM_EXTRA} " "${PARAM_REEXEC_ON_FAILURE}"
|
|
|
|
local EXPECT_SHUTDOWN
|
|
EXPECT_SHUTDOWN=${NESTED_EXPECT_SHUTDOWN:-}
|
|
|
|
if [ "$EXPECT_SHUTDOWN" != "1" ]; then
|
|
# Wait until the vm is ready to receive connections
|
|
if ! nested_wait_vm_ready 120; then
|
|
echo "failed to wait for the vm becomes ready to receive connections"
|
|
return 1
|
|
fi
|
|
# Wait for the snap command to be available
|
|
nested_wait_for_snap_command 120 1
|
|
# Wait for snap seeding to be done
|
|
# retry this wait command up to 3 times since we sometimes see races
|
|
# where the snap command appears, then immediately disappears and then
|
|
# re-appears immediately after and so the next command fails
|
|
attempts=0
|
|
until remote.exec "sudo snap wait system seed.loaded"; do
|
|
attempts=$(( attempts + 1))
|
|
if [ "$attempts" = 3 ]; then
|
|
echo "failed to wait for snap wait command to return successfully"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
# Copy tools to be used on tests
|
|
nested_prepare_tools
|
|
# Wait for cloud init to be done if the system is using cloud-init
|
|
if [ "$NESTED_USE_CLOUD_INIT" = true ]; then
|
|
if ! remote.exec "retry --wait 1 -n 5 sh -c 'cloud-init status --wait'"; then
|
|
# Error 2 means 'recoverable error', ignore that case
|
|
ret=0
|
|
remote.exec "cloud-init status" || ret=$?
|
|
if [ "$ret" -ne 0 ] && [ "$ret" -ne 2 ]; then
|
|
echo "cloud-init finished with error $ret"
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nested_get_current_image_name() {
|
|
echo "ubuntu-core-current.img"
|
|
}
|
|
|
|
nested_start_core_vm() {
|
|
local CURRENT_IMAGE CURRENT_NAME
|
|
CURRENT_NAME="$(nested_get_current_image_name)"
|
|
CURRENT_IMAGE="$NESTED_IMAGES_DIR/$CURRENT_NAME"
|
|
|
|
# In case the current image already exists, it needs to be reused and in that
|
|
# case is neither required to copy the base image nor prepare the ssh
|
|
if [ ! -f "$CURRENT_IMAGE" ]; then
|
|
# As core18 systems use to fail to start the assertion disk when using the
|
|
# snapshot feature, we copy the original image and use that copy to start
|
|
# the VM.
|
|
# Some tests however need to force stop and restart the VM with different
|
|
# options, so if that env var is set, we will reuse the existing file if it
|
|
# exists
|
|
local IMAGE_NAME
|
|
IMAGE_NAME="$(nested_get_image_name core)"
|
|
if ! [ -f "$NESTED_IMAGES_DIR/$IMAGE_NAME" ]; then
|
|
echo "No image found to be started"
|
|
exit 1
|
|
fi
|
|
|
|
# images are created as sparse files, simple cp should preserve that
|
|
# property
|
|
cp -v "$NESTED_IMAGES_DIR/$IMAGE_NAME" "$CURRENT_IMAGE"
|
|
|
|
# Start the nested core vm
|
|
nested_start_core_vm_unit "$CURRENT_IMAGE"
|
|
|
|
if [ ! -f "$NESTED_IMAGES_DIR/$IMAGE_NAME.configured" ]; then
|
|
# configure ssh for first time
|
|
nested_prepare_ssh
|
|
sync
|
|
|
|
# keep a copy of the current image if it is a generic image
|
|
if nested_is_generic_image && [ "$NESTED_CONFIGURE_IMAGES" = "true" ]; then
|
|
# Stop the current image and compress it
|
|
nested_shutdown
|
|
|
|
# Save the image with the name of the original image
|
|
cp -v "${CURRENT_IMAGE}" "$NESTED_IMAGES_DIR/$IMAGE_NAME"
|
|
touch "$NESTED_IMAGES_DIR/$IMAGE_NAME.configured"
|
|
|
|
# Start the current image again and wait until it is ready
|
|
nested_start
|
|
fi
|
|
fi
|
|
else
|
|
# Start the nested core vm
|
|
nested_start_core_vm_unit "$CURRENT_IMAGE"
|
|
fi
|
|
}
|
|
|
|
nested_shutdown() {
|
|
# we sometimes have bugs in nested vm's where files that were successfully
|
|
# written become empty all of a sudden, so doing a sync here in the VM, and
|
|
# another one in the host when done probably helps to avoid that, and at
|
|
# least can't hurt anything
|
|
remote.exec "sync"
|
|
remote.exec "sudo shutdown now" || true
|
|
nested_wait_for_no_ssh 120 1
|
|
nested_force_stop_vm
|
|
tests.systemd wait-for-service -n 30 --wait 1 --state inactive "$NESTED_VM"
|
|
sync
|
|
}
|
|
|
|
nested_start() {
|
|
nested_save_serial_log
|
|
nested_force_start_vm
|
|
tests.systemd wait-for-service -n 30 --wait 1 --state active "$NESTED_VM"
|
|
nested_wait_for_ssh 300 1
|
|
nested_prepare_tools
|
|
}
|
|
|
|
nested_force_restart_vm() {
|
|
nested_force_stop_vm
|
|
nested_force_start_vm
|
|
tests.systemd wait-for-service -n 30 --wait 1 --state active "$NESTED_VM"
|
|
}
|
|
|
|
nested_create_classic_vm() {
|
|
local IMAGE_NAME
|
|
IMAGE_NAME="$(nested_get_image_name classic)"
|
|
|
|
mkdir -p "$NESTED_IMAGES_DIR"
|
|
if [ ! -f "$NESTED_IMAGES_DIR/$IMAGE_NAME" ]; then
|
|
# shellcheck source=tests/lib/image.sh
|
|
. "$TESTSLIB"/image.sh
|
|
|
|
# Get the cloud image
|
|
local IMAGE_URL
|
|
IMAGE_URL="$(get_image_url_for_vm)"
|
|
wget -q -P "$NESTED_IMAGES_DIR" "$IMAGE_URL"
|
|
nested_download_image "$IMAGE_URL" "$IMAGE_NAME"
|
|
|
|
# Prepare the cloud-init configuration and configure image
|
|
nested_create_cloud_init_config "$NESTED_ASSETS_DIR/seed"
|
|
cloud-localds -H "$(hostname)" "$NESTED_ASSETS_DIR/seed.img" "$NESTED_ASSETS_DIR/seed"
|
|
fi
|
|
|
|
# Save a copy of the image
|
|
cp -v "$NESTED_IMAGES_DIR/$IMAGE_NAME" "$NESTED_IMAGES_DIR/$IMAGE_NAME.pristine"
|
|
}
|
|
|
|
nested_start_classic_vm() {
|
|
local IMAGE QEMU IMAGE_NAME
|
|
QEMU="$(nested_qemu_name)"
|
|
IMAGE_NAME="$(nested_get_image_name classic)"
|
|
|
|
if [ ! -f "$NESTED_IMAGES_DIR/$IMAGE_NAME" ] ; then
|
|
cp -v "$NESTED_IMAGES_DIR/$IMAGE_NAME.pristine" "$IMAGE_NAME"
|
|
fi
|
|
# Give extra disk space for the image
|
|
qemu-img resize "$NESTED_IMAGES_DIR/$IMAGE_NAME" +2G
|
|
|
|
# Now qemu parameters are defined
|
|
local PARAM_SMP PARAM_MEM
|
|
PARAM_SMP="-smp 1"
|
|
# use only 2G of RAM for qemu-nested
|
|
if [ "$SPREAD_BACKEND" = "google-nested" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-4096}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-2}"
|
|
elif [ "$SPREAD_BACKEND" = "google-nested-dev" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-8192}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-4}"
|
|
elif [ "$SPREAD_BACKEND" = "qemu-nested" ]; then
|
|
PARAM_MEM="-m ${NESTED_MEM:-2048}"
|
|
PARAM_SMP="-smp ${NESTED_CPUS:-1}"
|
|
else
|
|
echo "unknown spread backend $SPREAD_BACKEND"
|
|
exit 1
|
|
fi
|
|
local PARAM_DISPLAY PARAM_NETWORK PARAM_MONITOR PARAM_USB PARAM_CPU PARAM_CD PARAM_RANDOM PARAM_SNAPSHOT
|
|
PARAM_DISPLAY="-nographic"
|
|
PARAM_NETWORK="-net nic,model=virtio -net user,hostfwd=tcp::$NESTED_SSH_PORT-:22"
|
|
PARAM_MONITOR="-monitor tcp:127.0.0.1:$NESTED_MON_PORT,server=on,wait=off"
|
|
PARAM_USB="-usb"
|
|
PARAM_CPU=""
|
|
PARAM_CD="${NESTED_PARAM_CD:-}"
|
|
PARAM_RANDOM="-object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0"
|
|
# TODO: can this be removed? we create a "pristine" copy above?
|
|
#PARAM_SNAPSHOT="-snapshot"
|
|
PARAM_SNAPSHOT=""
|
|
PARAM_EXTRA="${NESTED_PARAM_EXTRA:-}"
|
|
|
|
# XXX: duplicated from nested core vm
|
|
# Set kvm attribute
|
|
local ATTR_KVM
|
|
ATTR_KVM=""
|
|
if nested_is_kvm_enabled; then
|
|
ATTR_KVM=",accel=kvm"
|
|
# CPU can be defined just when kvm is enabled
|
|
PARAM_CPU="-cpu host"
|
|
fi
|
|
|
|
local PARAM_MACHINE PARAM_IMAGE PARAM_SEED PARAM_SERIAL PARAM_BIOS PARAM_TPM
|
|
if [[ "$SPREAD_BACKEND" = google-nested* ]]; then
|
|
PARAM_MACHINE="-machine ubuntu,accel=kvm"
|
|
PARAM_CPU="-cpu host"
|
|
elif [ "$SPREAD_BACKEND" = "qemu-nested" ]; then
|
|
# check if we have nested kvm
|
|
if [ "$(cat /sys/module/kvm_*/parameters/nested)" = "1" ]; then
|
|
PARAM_MACHINE="-machine ubuntu${ATTR_KVM}"
|
|
else
|
|
# and if not reset kvm related parameters
|
|
PARAM_MACHINE=""
|
|
PARAM_CPU=""
|
|
ATTR_KVM=""
|
|
fi
|
|
else
|
|
echo "unknown spread backend $SPREAD_BACKEND"
|
|
exit 1
|
|
fi
|
|
|
|
PARAM_IMAGE="-drive file=$NESTED_IMAGES_DIR/$IMAGE_NAME,if=none,id=disk1 -device virtio-blk-pci,drive=disk1,bootindex=1"
|
|
PARAM_SEED="-drive file=$NESTED_ASSETS_DIR/seed.img,if=virtio"
|
|
# Open port 7777 on the host so that failures in the nested VM (e.g. to
|
|
# create users) can be debugged interactively via
|
|
# "telnet localhost 7777". Also keeps the logs
|
|
#
|
|
# XXX: should serial just be logged to stdout so that we just need
|
|
# to "journalctl -u $NESTED_VM" to see what is going on ?
|
|
if "$QEMU" -version | grep '2\.5'; then
|
|
# XXX: remove once we no longer support xenial hosts
|
|
PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log"
|
|
else
|
|
PARAM_SERIAL="-chardev socket,telnet=on,host=localhost,server=on,port=7777,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0"
|
|
fi
|
|
PARAM_BIOS=""
|
|
PARAM_TPM=""
|
|
|
|
# ensure we have a log dir
|
|
mkdir -p "$NESTED_LOGS_DIR"
|
|
# save logs from previous runs
|
|
nested_save_serial_log
|
|
|
|
# Systemd unit is created, it is important to respect the qemu parameters
|
|
# order
|
|
tests.systemd create-and-start-unit "$NESTED_VM" "${QEMU} \
|
|
${PARAM_SMP} \
|
|
${PARAM_CPU} \
|
|
${PARAM_MEM} \
|
|
${PARAM_SNAPSHOT} \
|
|
${PARAM_MACHINE} \
|
|
${PARAM_DISPLAY} \
|
|
${PARAM_NETWORK} \
|
|
${PARAM_BIOS} \
|
|
${PARAM_TPM} \
|
|
${PARAM_RANDOM} \
|
|
${PARAM_IMAGE} \
|
|
${PARAM_SEED} \
|
|
${PARAM_SERIAL} \
|
|
${PARAM_MONITOR} \
|
|
${PARAM_USB} \
|
|
${PARAM_EXTRA} \
|
|
${PARAM_CD} "
|
|
|
|
if ! nested_wait_vm_ready 60; then
|
|
echo "failed to wait for the vm becomes ready to receive connections"
|
|
return 1
|
|
fi
|
|
|
|
# Copy tools to be used on tests
|
|
nested_prepare_tools
|
|
}
|
|
|
|
nested_destroy_vm() {
|
|
tests.systemd stop-unit --remove "$NESTED_VM"
|
|
|
|
local CURRENT_IMAGE
|
|
CURRENT_IMAGE="$NESTED_IMAGES_DIR/$(nested_get_current_image_name)"
|
|
rm -f "$CURRENT_IMAGE"
|
|
}
|
|
|
|
nested_status_vm() {
|
|
systemctl status "$NESTED_VM" || true
|
|
}
|
|
|
|
remote.exec_as() {
|
|
local USER="$1"
|
|
local PASSWD="$2"
|
|
shift 2
|
|
sshpass -p "$PASSWD" ssh -p "$NESTED_SSH_PORT" -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$USER"@localhost "$@"
|
|
}
|
|
|
|
nested_prepare_tools() {
|
|
TOOLS_PATH=/writable/test-tools
|
|
if ! remote.exec "test -d $TOOLS_PATH" &>/dev/null; then
|
|
remote.exec "sudo mkdir -p $TOOLS_PATH"
|
|
remote.exec "sudo chown user1:user1 $TOOLS_PATH"
|
|
fi
|
|
|
|
if ! remote.exec "test -e $TOOLS_PATH/retry" &>/dev/null; then
|
|
remote.push "$TESTSTOOLS/retry"
|
|
remote.exec "mv retry $TOOLS_PATH/retry"
|
|
fi
|
|
|
|
if ! remote.exec "test -e $TOOLS_PATH/not" &>/dev/null; then
|
|
remote.push "$TESTSTOOLS/not"
|
|
remote.exec "mv not $TOOLS_PATH/not"
|
|
fi
|
|
|
|
if ! remote.exec "test -e $TOOLS_PATH/MATCH" &>/dev/null; then
|
|
# shellcheck source=tests/lib/spread-funcs.sh
|
|
. "$TESTSLIB"/spread-funcs.sh
|
|
echo '#!/bin/bash' > MATCH_FILE
|
|
type MATCH | tail -n +2 >> MATCH_FILE
|
|
echo 'MATCH "$@"' >> MATCH_FILE
|
|
chmod +x MATCH_FILE
|
|
remote.push "MATCH_FILE"
|
|
remote.exec "mv MATCH_FILE $TOOLS_PATH/MATCH"
|
|
rm -f MATCH_FILE
|
|
fi
|
|
|
|
if ! remote.exec "test -e $TOOLS_PATH/NOMATCH" &>/dev/null; then
|
|
# shellcheck source=tests/lib/spread-funcs.sh
|
|
. "$TESTSLIB"/spread-funcs.sh
|
|
echo '#!/bin/bash' > NOMATCH_FILE
|
|
type NOMATCH | tail -n +2 >> NOMATCH_FILE
|
|
echo 'NOMATCH "$@"' >> NOMATCH_FILE
|
|
chmod +x NOMATCH_FILE
|
|
remote.push "NOMATCH_FILE"
|
|
remote.exec "mv NOMATCH_FILE $TOOLS_PATH/NOMATCH"
|
|
rm -f NOMATCH_FILE
|
|
fi
|
|
|
|
if ! remote.exec "grep -qE PATH=.*$TOOLS_PATH /etc/environment"; then
|
|
# shellcheck disable=SC2016
|
|
REMOTE_PATH="$(remote.exec 'echo $PATH')"
|
|
remote.exec "echo PATH=$TOOLS_PATH:$REMOTE_PATH | sudo tee -a /etc/environment"
|
|
fi
|
|
}
|
|
|
|
nested_add_tty_chardev() {
|
|
local CHARDEV_ID=$1
|
|
local CHARDEV_PATH=$2
|
|
echo "chardev-add file,path=$CHARDEV_PATH,id=$CHARDEV_ID" | nc -q 0 127.0.0.1 "$NESTED_MON_PORT"
|
|
echo "chardev added"
|
|
}
|
|
|
|
nested_remove_chardev() {
|
|
local CHARDEV_ID=$1
|
|
echo "chardev-remove $CHARDEV_ID" | nc -q 0 127.0.0.1 "$NESTED_MON_PORT"
|
|
echo "chardev added"
|
|
}
|
|
|
|
nested_add_usb_serial_device() {
|
|
local DEVICE_ID=$1
|
|
local CHARDEV_ID=$2
|
|
local SERIAL_NUM=$3
|
|
echo "device_add usb-serial,chardev=$CHARDEV_ID,id=$DEVICE_ID,serial=$SERIAL_NUM" | nc -q 0 127.0.0.1 "$NESTED_MON_PORT"
|
|
echo "device added"
|
|
}
|
|
|
|
nested_del_device() {
|
|
local DEVICE_ID=$1
|
|
echo "device_del $DEVICE_ID" | nc -q 0 127.0.0.1 "$NESTED_MON_PORT"
|
|
echo "device deleted"
|
|
}
|
|
|
|
nested_get_core_revision_for_channel() {
|
|
local CHANNEL=$1
|
|
remote.exec "snap info core" | awk "/${CHANNEL}: / {print(\$4)}" | sed -e 's/(\(.*\))/\1/'
|
|
}
|
|
|
|
nested_get_core_revision_installed() {
|
|
remote.exec "snap info core" | awk "/installed: / {print(\$3)}" | sed -e 's/(\(.*\))/\1/'
|
|
}
|
|
|
|
nested_fetch_spread() {
|
|
if [ ! -f "$NESTED_WORK_DIR/spread" ]; then
|
|
mkdir -p "$NESTED_WORK_DIR"
|
|
curl -s https://storage.googleapis.com/snapd-spread-tests/spread/spread-amd64.tar.gz | tar -xz -C "$NESTED_WORK_DIR"
|
|
# make sure spread really exists
|
|
test -x "$NESTED_WORK_DIR/spread"
|
|
fi
|
|
echo "$NESTED_WORK_DIR/spread"
|
|
}
|
|
|
|
nested_build_seed_cdrom() {
|
|
local SEED_DIR="$1"
|
|
local SEED_NAME="$2"
|
|
local LABEL="$3"
|
|
|
|
shift 3
|
|
|
|
local ORIG_DIR=$PWD
|
|
|
|
pushd "$SEED_DIR" || return 1
|
|
genisoimage -output "$ORIG_DIR/$SEED_NAME" -volid "$LABEL" -joliet -rock "$@"
|
|
popd || return 1
|
|
}
|
|
|
|
nested_wait_for_device_initialized_change() {
|
|
local retry=60
|
|
local wait=1
|
|
|
|
while ! remote.exec "snap changes" | MATCH "Done.*Initialize device"; do
|
|
retry=$(( retry - 1 ))
|
|
if [ $retry -le 0 ]; then
|
|
echo "Timed out waiting for device to be fully initialized. Aborting!"
|
|
return 1
|
|
fi
|
|
sleep "$wait"
|
|
done
|
|
}
|
|
|
|
nested_check_spread_results() {
|
|
SPREAD_LOG=$1
|
|
if [ -z "$SPREAD_LOG" ]; then
|
|
return 1
|
|
fi
|
|
|
|
if grep -eq "Successful tasks:" "$SPREAD_LOG"; then
|
|
if grep -E "Failed (task|suite|project)" "$SPREAD_LOG"; then
|
|
return 1
|
|
fi
|
|
if ! grep -eq "Aborted tasks: 0" "$SPREAD_LOG"; then
|
|
return 1
|
|
fi
|
|
|
|
if [ "$EXIT_STATUS" = "0" ]; then
|
|
return 0
|
|
fi
|
|
else
|
|
return 1
|
|
fi
|
|
}
|