#!/bin/sh -ex
# Environment variables:
# * DOMAIN: default is downloads.osmocom.org, set to people.osmocom.org for testing pkgs from home:…
# * FEED: binary package feed (e.g. "latest", "nightly")
# * INTERACTIVE: set to 1 to keep an interactive shell open after the script ran (for debugging)
# * KEEP_VM: for development: don't kill/start VM if still running
# * PROJ: OBS project namespace (e.g. "osmocom:latest")
# * PROJ_CONFLICT: Conflicting OBS project namespace (e.g. "osmocom:nightly")
# * SKIP_PREPARE_VM: for development, skip the prepare_vm code
# * TESTS: which tests to run (all by default, see below for possible values)
. "$(dirname "$0")/common.sh"

DOMAIN="${DOMAIN:-downloads.osmocom.org}"
DISTRO="$1"
DISTROS="
	centos8
	debian10
	debian11
	debian12
"
IMG_DIR="/opt/qemu"
TEST_DIR="scripts/repo-install-test"
IMG_PATH="_repo_install_test_data/temp.qcow2"
PID_FILE="_repo_install_test_data/qemu.pid"
PORT_FILE="_repo_install_test_data/qemu.port"
LOG_FILE="_repo_install_test_data/qemu.log"

check_usage() {
	local i
	for i in $DISTROS; do
		if [ "$DISTRO" = "$i" ]; then
			return
		fi
	done
	set +x
	echo
	echo "usage: repo-install-test.sh DISTRO"
	echo "DISTRO: one of: $DISTROS"
	exit 1
}

get_backing_img_path() {
	local ret=""

	case "$DISTRO" in
	centos8)
		ret="$IMG_DIR/alma-8.5.qcow2"
		;;
	debian10)
		ret="$IMG_DIR/debian-10.qcow2"
		;;
	debian11)
		ret="$IMG_DIR/debian-11.qcow2"
		;;
	debian12)
		ret="$IMG_DIR/debian-12.qcow2"
		;;
	*)
		set +x
		echo "ERROR: script error, missing img path for $DISTRO" >&2
		exit 1
		;;
	esac

	if [ -e "$ret" ]; then
		echo "$ret"
	else
		set +x
		echo "ERROR: file not found: $ret" >&2
		echo "ERROR: qemu images not installed via ansible?" >&2
		exit 1
	fi
}

find_free_ssh_port() {
	SSH_PORT="$(echo "($PPID % 1000) + 22022" | bc)"
	while nc -z 127.0.0.1 "$SSH_PORT"; do
		SSH_PORT=$((SSH_PORT + 1))
	done

	echo "$SSH_PORT" > "$PORT_FILE"
}

prepare_img() {
	mkdir -p "$(dirname "$IMG_PATH")"

	qemu-img \
		create \
		-f qcow2 \
		-b "$(get_backing_img_path)" \
		-F qcow2 \
		"$IMG_PATH"
}

qemu_start() {
	if [ -n "$KEEP_VM" ] && [ -e "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")"; then
		SSH_PORT="$(cat "$PORT_FILE")"
		return
	fi

	prepare_img
	find_free_ssh_port

	(timeout 1h qemu-system-x86_64 \
		-cpu host \
		-device "virtio-net-pci,netdev=net" \
		-display none \
		-drive "file=$IMG_PATH,format=qcow2" \
		-enable-kvm \
		-m 1024 \
		-netdev "user,id=net,hostfwd=tcp:127.0.0.1:$SSH_PORT-:22" \
		-nodefaults \
		-pidfile "$PID_FILE" \
		-serial stdio \
		-smp 16 >"$LOG_FILE" 2>&1) &
}

qemu_ssh() {
	timeout "${TIMEOUT:-1m}" \
		sshpass -p root \
		ssh \
			-p "$SSH_PORT" \
			-o StrictHostKeyChecking=no \
			-o UserKnownHostsFile=/dev/null \
			root@127.0.0.1 \
			-- \
			"$@"
}

qemu_scp() {
	timeout "${TIMEOUT:-1m}" \
		sshpass -p root \
		scp \
			-P "$SSH_PORT" \
			-o StrictHostKeyChecking=no \
			-o UserKnownHostsFile=/dev/null \
			"$@"
}

qemu_prepare_vm() {
	case "$DISTRO" in
	centos8)
		# https://almalinux.org/blog/2023-12-20-almalinux-8-key-update/
		qemu_ssh dnf upgrade -y almalinux-release
		;;
	esac
}

qemu_run_test_script() {
	cat <<- EOF > "$TEST_DIR/run-inside-env.sh"
	#!/bin/sh -ex

	export DISTRO="$DISTRO"
	export DOMAIN="$DOMAIN"
	export FEED="$FEED"
	export PROJ="$PROJ"
	export PROJ_CONFLICT="$PROJ_CONFLICT"
	export SKIP_PREPARE_VM="$SKIP_PREPARE_VM"
	export TESTS="$TESTS"

	/repo-install-test/run-inside.sh
	EOF

	qemu_ssh rm -rf /repo-install-test/
	qemu_ssh mkdir /repo-install-test
	qemu_scp -r "$TEST_DIR"/* "root@127.0.0.1:/repo-install-test"

	TIMEOUT="1h" qemu_ssh sh -ex /repo-install-test/run-inside-env.sh
}

qemu_print_log() {
	echo
	echo "Contents of $LOG_FILE:"
	echo
	cat "$LOG_FILE"
	echo
	echo "---"
	echo "NOTE: If you have just set up a new jenkins node, and get the"
	echo "error 'Could not access KVM kernel module: Permission denied',"
	echo "then you probably need to disconnect jenkins, connect it again"
	echo "and retry."
	echo "---"
	echo
}

qemu_ssh_wait() {
	set +x
	echo
	echo "Waiting for VM to boot up..."
	echo
	set -x

	# PID file does not get created immediately
	sleep 1
	local pid="$(cat "$PID_FILE")"

	for i in $(seq 1 6); do
		if [ -z "$pid" ] || ! kill -0 "$pid"; then
			set +x
			echo "ERROR: qemu failed, pid: $pid"
			qemu_print_log
			exit 1
		fi

		if TIMEOUT=10s qemu_ssh true; then
			return
		fi

		sleep 1
	done

	set +x
	echo "ERROR: timeout, VM did not boot up. Log file contents:"
	qemu_print_log
	exit 1
}

clean_up() {
	if [ -n "$KEEP_VM" ]; then
		return
	fi

	if [ -e "$PID_FILE" ]; then
		kill $(cat "$PID_FILE") || true
	fi

	rm -f "$IMG_PATH"
}

clean_up_trap() {
	if [ -n "$INTERACTIVE" ]; then
		TIMEOUT="1h" qemu_ssh bash -i
	fi

	set +x
	echo
	echo "### Clean up ###"
	echo
	set -x

	trap - EXIT INT TERM 0

	clean_up
}

check_usage

FEED="${FEED:-nightly}"
PROJ="${PROJ:-osmocom:$FEED}"

if [ -z "$TESTS" ]; then
	TESTS="
		test_conflict
		install_repo_packages
		test_binaries
		services_check
	"
fi

if [ -z "$PROJ_CONFLICT" ]; then
	case "$FEED" in
		latest)
			PROJ_CONFLICT="osmocom:nightly"
			;;
		nightly)
			PROJ_CONFLICT="osmocom:latest"
			;;
		next)
			PROJ_CONFLICT="osmocom:nightly"
			;;
	esac
fi


clean_up
trap clean_up_trap EXIT INT TERM 0

qemu_start
qemu_ssh_wait


set +x
echo
echo "VM is running!"
echo
set -x

qemu_prepare_vm
qemu_run_test_script