#!/bin/bash
# SPDX-License-Identifier: MulanPSL-2.0+
# Copyright (c) 2020 Huawei Technologies Co., Ltd. All rights reserved.

set -e

umask 0002

. $LKP_SRC/lib/env.sh

# allow sut os array
# - if job's os not in this array, job won't execute
ALLOW_OS=(
	openeuler
)

WORKSPACE="/usr/local/iso2rootfs"
ENV_DIR="${WORKSPACE}/env"
I2Q_SRC="${WORKSPACE}/iso2qcow2"
CCI_SRC="${WORKSPACE}/compass-ci"

log_info()
{
	echo "[INFO] $*"
}

exit_info()
{
	log_info "$@"
	exit 0
}

die()
{
	echo "[ERROR] $*" >&2
	exit 1
}

############ pre works ############
check_yaml_vars()
{
	log_info "starting check yaml vars ..."

	local yaml_vars=(
		"os"
		"os_arch"
		"os_version"
		"iso_os"
		"iso_arch"
		"iso_version"
		"rootfs_protocol"
		"rootfs_server"
		"rootfs_path"
	)

	for yaml_t in "${yaml_vars[@]}"
	do
		[ -n "$(eval echo "\$${yaml_t}")" ] || die "cannot find value of var: ${yaml_t}."
	done
}

check_iso_name()
{
	# Why need this step:
	#
	# 1. Our current strategy for detecting iso updates is as follows:
	#    - Monitor iso releases for different os (such as Openeuler).
	#    - Openeuler currently provides an http release_iso file that is
	#      updated each time a new iso is released. we can get the
	#      following two contents from this file:
	#      - Latest iso url;
	#      - Latest iso sha256sum; # Can be obtained by splicing:
	#        {latest_iso_url}. sha256sum
	#
	# 2. Regarding the iso name in the iso url, I consulted the support
	#    staff of openeuler's iso release and got the following reply:
	#    - There are currently two kinds of iso names:
	#      - openEuler-2.0-SP8-xxx.iso;
	#      - openEuler-20.03-xxx.iso;
	#    - openEuler-2.0.SP8-xxx. iso is not used for dailybuild;
	#    - Not sure if new uses of iso will be added in the future;
	#
	# 3. So we've prepared an array to fill in iso names to skip and
	#    exclude any additions later.

	local iso_prefixes_to_skip
	case ${iso_os} in
		"openeuler")
			iso_prefixes_to_skip=("openEuler-2.0-SP8")
		;;
		*)
			return
		;;
	esac

	local prefix
	for prefix in "${iso_prefixes_to_skip[@]}"
	do
		[[ ${ISO_NAME} != ${prefix}* ]] ||
			exit_info "${iso_os} haven't release new iso for openEuler, no need to generate rootfs"
	done
}

get_daily_iso_checksum()
{
	ISO_URL="$(curl "${dailybuild_iso_url_file}")"
	[ "${iso_os}" == "openeuler" ] && {
		local pub_ip=$(echo "${dailybuild_iso_url_file}" | grep -oEw "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}")
		ISO_URL="$(curl "${dailybuild_iso_url_file}" | sed -r "s/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/${pub_ip}/g")"
	}

	ISO_NAME=$(basename "$ISO_URL")
	ISO_CHECKSUM_URL="${ISO_URL}.sha256sum"
	check_iso_name

	curl -o "${CHECKSUM_FILE_CACHE}.tmp" "${ISO_CHECKSUM_URL}"
	SHA256SUM_NET=$(awk '{print $1}' "${CHECKSUM_FILE_CACHE}.tmp")
}

mount_rootfs()
{
	case ${rootfs_protocol} in
		"nfs")
			ROOTFS_SERVER_PATH="${rootfs_server}:${rootfs_path}/${iso_os}/${iso_arch}"
			install_pkgs "nfs-utils"
		;;
		"cifs")
			ROOTFS_SERVER_PATH="//${rootfs_server}/${rootfs_path}/${iso_os}/${iso_arch}"
			ROOTFS_MOUNT_PARAM="guest,vers=1.0,noacl,nouser_xattr"
			install_pkgs "cifs-utils"
		;;
		*)
			die "cannot support the rootfs_protocol: ${rootfs_protocol}."
		;;
	esac

	ROOTFS_LOCAL_PATH="${WORKSPACE}/compass_os_${iso_os}_${iso_arch}"

	[ -d "${ROOTFS_LOCAL_PATH}" ] || mkdir -p "$ROOTFS_LOCAL_PATH"
	if [ -n "${ROOTFS_MOUNT_PARAM}" ]
	then
		mount -t ${rootfs_protocol} -o "${ROOTFS_MOUNT_PARAM}" "${ROOTFS_SERVER_PATH}" "${ROOTFS_LOCAL_PATH}"
	else
		mount -t ${rootfs_protocol} "${ROOTFS_SERVER_PATH}" "${ROOTFS_LOCAL_PATH}"
	fi
}

get_cache_iso_checksum()
{
	CHECKSUM_FILE_CACHE="${ROOTFS_LOCAL_PATH}/${iso_version}-latest.sha256sum"
	[ -f "${CHECKSUM_FILE_CACHE}" ] || return 0
	SHA256SUM_CACHE=$(awk '{print $1}' "$CHECKSUM_FILE_CACHE")
}

check_sha256sum_update()
{
	[ -n "${dailybuild_iso_url_file}" ] || return 0

	get_cache_iso_checksum
	get_daily_iso_checksum

	if [ -n "$SHA256SUM_CACHE" ]
	then
		[ -n "$SHA256SUM_NET" ] || die "cannot get sha256sum of dailybuild iso."

		if [ "$SHA256SUM_CACHE" == "$SHA256SUM_NET" ]
		then
			rm -f "${CHECKSUM_FILE_CACHE}.tmp"
			exit_info "${iso_os} haven't release new iso, no need to generate rootfs"
		else
			log_info "${iso_os} release a new iso, start to generate rootfs ..."
			return
		fi

	else
		log_info "${CHECKSUM_FILE_CACHE} doesn't exist, start to generate rootfs ..."
		return
	fi
}

check_os()
{
	log_info "starting check os ..."

	echo "${ALLOW_OS[@]}" | grep -wq "${os}" ||
		die "current os is not in allow os.
			current os: ${os}.
			allow os: \(${ALLOW_OS[@]}\)"
}

get_cgz()
{
	local cgz_url="http://${INITRD_HTTP_HOST}:${INITRD_HTTP_PORT}/initrd/pkg/nfs/${os}/${os_arch}/${os_version}/iso2rootfs.cgz"
	local cgz_name
	cgz_name=$(basename "${cgz_url}")
	local cgz_path_t=${ENV_DIR}/${cgz_name}
	CGZ_PATH=${ENV_DIR}/${cgz_name%.cgz}

	[ -d "$ENV_DIR" ] || mkdir -p "$ENV_DIR"
	[ -f "${cgz_path_t}" ] && rm -f "${cgz_path_t}"
	wget -c -O "${cgz_path_t}" "${cgz_url}"

	[ -d "$CGZ_PATH" ] && rm -rf "$CGZ_PATH"
	mkdir -p "$CGZ_PATH"
	tar -xf "${cgz_path_t}" -C "${CGZ_PATH}"
}

get_pkg_installer()
{
	local installer

	has_cmd yum && installer=yum
	has_cmd apt && installer=apt

	[ -n "$installer" ] || die "cannot find pkg installer."
	echo $installer
}

install_pkgs()
{
	local installer=$(get_pkg_installer)

	for pt
	do
		"$installer" install -y "$pt" || die "cannot install pkg: $pt."
	done
}

config_git_proxy()
{
	if [[ -n ${GITCACHE_HOST} ]] && [[ -n ${GITCACHE_PORT} ]]
	then
		git config --system url."http://${GITCACHE_HOST}:${GITCACHE_PORT}/".insteadOf "https://"
	fi
}

config_iso2rootfs()
{
	log_info "starting config iso2rootfs env ..."
	install_pkgs "wget"
	get_cgz

	source "${CGZ_PATH}/config"

	install_pkgs "git"
	config_git_proxy
}

pre_works()
{
	check_yaml_vars

	mount_rootfs
	check_sha256sum_update

	check_os

	config_iso2rootfs
}

############ iso2qcow2 ############
download_iso2qcow2()
{
	[ -d "${I2Q_SRC}" ] && rm -rf "${I2Q_SRC}"
	git clone "$ISO2QCOW2_GIT_URL" "${I2Q_SRC}"
}

config_pip_proxy()
{
	if [[ -n ${SQUID_HOST} ]] && [[ -n ${SQUID_PORT} ]]
	then
		sed -i "s|^PIP_PROXY=|PIP_PROXY=\"http://$SQUID_HOST:$SQUID_PORT\"|g" "${I2Q_SRC}/conf/config"
	fi
}

config_iso_conf()
{
	local config_file="${I2Q_SRC}/conf/iso/iso.conf"
	sed -i "s|^OS=.*|OS=\"${iso_os}\"|g" "$config_file"
	sed -i "s|^OS_ARCH=.*|OS_ARCH=\"${iso_arch}\"|g" "$config_file"
	sed -i "s|^OS_VERSION=.*|OS_VERSION=\"${iso_version}\"|g" "$config_file"
}

config_iso_url()
{
	[ -n "${dailybuild_iso_url_file}" ] || return 0

	local i2q_iso_url_file="${I2Q_SRC}/conf/iso/net-iso/${iso_os}/${iso_arch}/${iso_version}"
	sed -i "s|^iso_url=.*|iso_url=\"${ISO_URL}\"|g" "${i2q_iso_url_file}"
	sed -i "s|^iso_checksum_url=.*|iso_checksum_url=\"${ISO_CHECKSUM_URL}\"|g" "${i2q_iso_url_file}"
}

config_iso2qcow2()
{
	log_info "starting config iso2qcow2 env ..."

	download_iso2qcow2

	config_pip_proxy
	config_iso_conf
	config_iso_url
}

run_iso2qcow2()
{
	log_info "starting run iso2qcow2 ..."

	${I2Q_SRC}/auto-install-iso.sh
}

############ qcow2rootfs ############
download_compass_ci()
{
	[ -d "${CCI_SRC}" ] && rm -rf "${CCI_SRC}"
	git clone "$COMPASS_CI_GIT_URL" "${CCI_SRC}"
}

config_rootfs_dir()
{
	ROOTFS_DES_DIR=${ROOTFS_LOCAL_PATH}/${iso_version}-$(date "+%Y-%m-%d-%H-%M-%S")
	[ -n "${dailybuild_iso_url_file}" ] && ROOTFS_DES_DIR=${ROOTFS_LOCAL_PATH}/${iso_version}-dailybuild-$(date "+%Y-%m-%d-%H-%M-%S")

	[ -d "${ROOTFS_DES_DIR}" ] &&
		ROOTFS_DES_DIR="${ROOTFS_DES_DIR}-${HOSTNAME##*--}"
	mkdir -p "$ROOTFS_DES_DIR"
}

config_qcow2rootfs()
{
	log_info "starting config qcow2rootfs env ..."

	download_compass_ci

	install_pkgs "docker"
	systemctl start docker

	config_rootfs_dir
	export CCI_SRC
	export HOME="/root"

	cd "${CCI_SRC}/container/qcow2rootfs"
	./build

	cd "${CCI_SRC}/container/dracut-initrd"
	./build
}

run_qcow2rootfs()
{
	log_info "starting run qcow2rootfs ..."

	local qcow2_path="$(ls ${I2Q_SRC}/output/*.qcow2)"

	cd "${CCI_SRC}/container/qcow2rootfs"
	./run "${qcow2_path}" "${ROOTFS_DES_DIR}"
}


############ config rootfs ############
config_dns_resolver()
{
	echo "${config_rootfs}" | grep -qw 'dns' || return 0

	grep -qs "^dns=none" $config_file ||
		sed -i '/^\[main\]/adns=none' "${ROOTFS_DES_DIR}/etc/NetworkManager/NetworkManager.conf"

	cat <<-EOF > "${ROOTFS_DES_DIR}/etc/resolv.conf"
	nameserver 114.114.114.114
	nameserver 8.8.8.8
	EOF
}

disable_selinux()
{
	echo "${config_rootfs}" | grep -qw 'no_selinux' || return 0
	sed -i 's/^SELINUX=.*/SELINUX=disabled/g' "${ROOTFS_DES_DIR}/etc/selinux/config"
}

disable_fstab()
{
	echo "${config_rootfs}" | grep -qw 'no_fstab' || return 0
	sed -i 's/^\([^#]\)/# \1/g' "${ROOTFS_DES_DIR}/etc/fstab"
}

install_pkgs_for_rootfs()
{
	[ -n "$rootfs_install_pkgs" ] || return 0

	local installer=$(get_pkg_installer)

	if [ "$installer" == "yum" ]; then
		"$installer" install -y --skip-broken --installroot=${ROOTFS_DES_DIR} ${rootfs_install_pkgs//,/ }
	elif [ "$installer" == "apt" ]; then
		"$installer" install -y --ignore-missing --fix-missing --fix-broken -o RootDir=${ROOTFS_DES_DIR} ${rootfs_install_pkgs//,/ }
	fi
}

enable_repos()
{
	echo "${config_rootfs}" | grep -qw 'enable_repos' || return 0

	ls ${ROOTFS_DES_DIR}/etc/yum.repos.d/*.repo > /dev/null 2>&1 && {
		sed -i 's/^enabled=0$/enabled=1/g' ${ROOTFS_DES_DIR}/etc/yum.repos.d/*.repo
	}

	return 0
}

config_rootfs()
{
	config_dns_resolver
	disable_selinux
	disable_fstab
	enable_repos

	install_pkgs_for_rootfs
}

############ test rootfs ############
get_qemu_efi_fd()
{
	source "$I2Q_SRC"/lib/tools/checker.sh
	source "$I2Q_SRC"/lib/os/hoster.sh
	source "$I2Q_SRC"/lib/libvirt/libvirt_vars.sh

	get_host_info
	get_efi_fd_path
}

test_rootfs()
{
	log_info "starting test rootfs ..."

	local test_script="${CGZ_PATH}/kvm.sh"

	local kernel_path=$(realpath "${ROOTFS_DES_DIR}"/boot/vmlinuz)
	local initrd_lkp_path=$(realpath "${ROOTFS_DES_DIR}"/initrd.lkp)
	[ -f "$kernel_path" ] || die "cannot find kernel of rootfs: $kernel_path"
	[ -f "$initrd_lkp_path" ] || die "cannot find initrd_lkp of rootfs: $initrd_lkp_path"

	local root_path
	case ${rootfs_protocol} in
		"nfs")
			root_path="${ROOTFS_SERVER_PATH}/$(basename "${ROOTFS_DES_DIR}")"
		;;
		"cifs")
			root_path="cifs:${ROOTFS_SERVER_PATH}/$(basename "${ROOTFS_DES_DIR}"),${ROOTFS_MOUNT_PARAM}"
		;;
		*)
			die "cannot support the rootfs_protocol: ${rootfs_protocol}."
		;;
	esac
	get_qemu_efi_fd

	sed -i "s|KERNEL|\"${kernel_path}\"|g" "$test_script"
	sed -i "s|INITRD_LKP|\"${initrd_lkp_path}\"|g" "$test_script"
	sed -i "s|ROOT|\"${root_path}\"|g" "$test_script"
	sed -i "s|QEMU_EFI_FD|\"${EFI_FD_PATH}\"|g" "$test_script"

	cd "$(dirname "${test_script}")"

	expect << EOF
		# timeout for kvm.sh
		set timeout 300

		spawn "/bin/bash"
		match_max 100000

		expect -exact "# " {
		send -- "./$(basename "${test_script}")\r"
		}

		expect {
			"localhost login: " {
				send_user "\[INFO\] test rootfs ok"
				exit 0
			}
			"server * not responding, timed out" {
				send_user "\[WARNING\] rootfs can start, but need to disable some services"
				exit 0
			}
			timeout {
				send_user "\[ERROR\] test rootfs time out"
				exit 1
			}
		}
EOF
}

############ submit test job yaml ############
generate_global_yaml()
{
	[ -n "${submit_name}" ] || die "cannot find value of var: submit_name"
	[ -n "${submit_email}" ] || die "cannot find value of var: submit_email"
	[ -n "${submit_token}" ] || die "cannot find value of var: submit_token"

	local config_yaml="/etc/compass-ci/defaults/sparrow.yaml"

	mkdir -p "$(dirname "${config_yaml}")"

	cat <<-EOF > "${config_yaml}"
	SCHED_HOST: ${SCHED_HOST}
	SCHED_PORT: ${SCHED_PORT}
	my_name: ${submit_name}
	my_email: ${submit_email}
	my_token: ${submit_token}
	EOF
}

generate_submit_append_str()
{
	[ "$#" -eq 2 ] || die "generate_submit_append_str: please give 2 parameters."
	echo "os=${iso_os} os_arch=${iso_arch} os_version=${iso_version}-dailybuild os_mount=$1 testbox=$2"
}

prepare_submit()
{
	local submit_dir="/tmp/iso2rootfs/submit_tmp"
	mkdir -p "${submit_dir}"

	local submit_git_dir="${submit_dir}/${test_git_url##*/}"
	submit_git_dir="${submit_git_dir%.git}"

	[ -d "${submit_git_dir}" ] || {
		cd "${submit_dir}"
		git clone "${test_git_url}"
	}

	local test_git_yaml="${submit_git_dir}/$(eval echo "\$${test_num}_git_yaml")"
	local test_git_script="${submit_git_dir}/$(eval echo "\$${test_num}_git_script")"

	[ -f "${test_git_yaml}" ] || die "cannot find file: ${test_num}_git_yaml."
	cp "${test_git_yaml}" "${LKP_SRC}/jobs/"

	[ -f "${test_git_script}" ] || die "cannot find file: ${test_num}_git_script."
	cp "${test_git_script}" "${LKP_SRC}/tests/"
}

submit_one_yaml()
{
	log_info "starting submit ${test_yaml} ..."

	local test_git_url="$(eval echo "\$${test_num}_git_url")"
	[ -z "${test_git_url}" ] || prepare_submit

	cd "${LKP_SRC}/jobs"
	[ -f "${test_yaml}" ] || die "cannot find yaml in LKP_SRC/jobs: ${test_yaml}."

	local test_os_mount="$(eval echo "\$${test_num}_os_mount")"
	local test_testbox="$(eval echo "\$${test_num}_testbox")"

	[ -n "${test_os_mount}" ] || die "cannot find value of var: ${test_num}_os_mount."
	[ -n "${test_testbox}" ] || die "cannot find value of var: ${test_num}_testbox."

	"${LKP_SRC}/sbin/submit" \
		$(generate_submit_append_str "${test_os_mount}" "${test_testbox}") \
		"${test_yaml}"

	log_info "submit ${test_yaml} finished"
}

submit_yamls()
{
	[ -n "$test1_yaml" ] || return 0

	log_info "starting submit test yamls ..."

	[ -d "${LKP_SRC}" ] || die "cannot find value of var: LKP_SRC."
	cd "${LKP_SRC}" && {
		chmod +x ./sbin/install-dependencies.sh
		./sbin/install-dependencies.sh

		generate_global_yaml
	}

	local test_yaml_index=1
	local test_num="test${test_yaml_index}"
	local test_yaml="$(eval echo "\$${test_num}_yaml")"

	while [ -n "${test_yaml}" ]
	do
		submit_one_yaml
		test_yaml_index=$((${test_yaml_index} +1))
		test_num="test${test_yaml_index}"
		test_yaml="$(eval echo "\$${test_num}_yaml")"
	done

	log_info "submit test yamls finished"
}

############ post works ############
update_dailybuild_soft_link()
{
	[ -n "${dailybuild_iso_url_file}" ] || return 0

	mv "${CHECKSUM_FILE_CACHE}.tmp" "${CHECKSUM_FILE_CACHE}"
	cp "${CHECKSUM_FILE_CACHE}" "${ROOTFS_DES_DIR}/SHA256SUM"

	local soft_link="${iso_version}-dailybuild"
	cd "${ROOTFS_LOCAL_PATH}" &&
		ln -sfT "$(basename "${ROOTFS_DES_DIR}")" "${soft_link}"
}

post_works()
{
	log_info "starting post works ..."

	update_dailybuild_soft_link

	cd / && umount "${ROOTFS_LOCAL_PATH}"

	log_info "iso2rootfs finished"
}

############ main ############
main()
{
	pre_works

	config_iso2qcow2
	run_iso2qcow2

	config_qcow2rootfs
	run_qcow2rootfs

	config_rootfs

	test_rootfs
	submit_yamls

	post_works
}

main
