#!/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="${rootfs_mount_param}"
			install_pkgs "cifs-utils"
		;;
		*)
			die "cannot support the rootfs_protocol: ${rootfs_protocol}."
		;;
	esac

	ROOTFS_LOCAL_PATH="${WORKSPACE}/rootfs/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
}

mount_initramfs()
{
	case ${initramfs_protocol} in
		"nfs")
			INITRAMFS_SERVER_PATH="${initramfs_server}:${initramfs_path}/${iso_os}/${iso_arch}"
			install_pkgs "nfs-utils"
		;;
		"cifs")
			INITRAMFS_SERVER_PATH="//${initramfs_server}/${initramfs_path}/${iso_os}/${iso_arch}"
			INITRAMFS_MOUNT_PARAM="${initramfs_mount_param}"
			install_pkgs "cifs-utils"
		;;
		*)
			die "cannot support the initramfs_protocol: ${initramfs_protocol}."
		;;
	esac

	INITRAMFS_LOCAL_PATH="${WORKSPACE}/initramfs/compass_os_${iso_os}_${iso_arch}"

	[ -d "${INITRAMFS_LOCAL_PATH}" ] || mkdir -p "$INITRAMFS_LOCAL_PATH"

	if [ -n "${INITRAMFS_MOUNT_PARAM}" ]
	then
		mount -t ${initramfs_protocol} -o "${INITRAMFS_MOUNT_PARAM}" "${INITRAMFS_SERVER_PATH}" "${INITRAMFS_LOCAL_PATH}"
	else
		mount -t ${initramfs_protocol} "${INITRAMFS_SERVER_PATH}" "${INITRAMFS_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_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 "git"

	# use $GIT_SERVER to direct git remote repo from gitee.com to our own git remote repo(by container git-daemon),
	# to make sure can use the latest code.
	if [ -n "$GIT_SERVER" ]; then
		cat >> /etc/gitconfig <<-EOF
		[url "git://$GIT_SERVER/gitee.com"]
			insteadOf=https://gitee.com
		EOF
	fi

	config_git_proxy
}

pre_works()
{
	check_yaml_vars

	mount_rootfs

	[ -n "$iso_url" ] && {
		ISO_URL=$iso_url
		ISO_CHECKSUM_URL="${ISO_URL}.sha256sum"
	}
	check_sha256sum_update

	check_os

	config_iso2rootfs
}

############ iso2qcow2 ############
download_iso2qcow2()
{
	[ -d "${I2Q_SRC}" ] && rm -rf "${I2Q_SRC}"
	git clone http://gitee.com/weijihui/iso2qcow2.git "${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()
{
	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 https://gitee.com/wu_fengguang/compass-ci.git "${CCI_SRC}"
}

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

	[ -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"

	mkdir -p /etc/systemd/system/docker.service.d
	cat >/etc/systemd/system/docker.service.d/10-ramdisk.conf <<-EOF
	[Service]
	Environment=DOCKER_RAMDISK=true
	EOF

	systemctl daemon-reload
	systemctl stop 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 > "${ROOTFS_DES_DIR}/etc/resolv.conf" <<-EOF
	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
}

############ submit test job yaml ############
prepare_lkp_client()
{
	log_info "starting prepare lkp client env..."

        ${LKP_SRC}/sbin/install-dependencies.sh

        export CCI_REPOS="/c"

        mkdir -p $CCI_REPOS && cd $CCI_REPOS
        git clone https://gitee.com/wu_fengguang/lab-z9.git || die "clone lab-z9 git repo failed"

        git clone https://gitee.com/wu_fengguang/lkp-tests.git || die "clone lkp-tests git repo failed"
        cp -a $LKP_SRC/. ./lkp-tests/
        export LKP_SRC=$(realpath ./lkp-tests/)

        local config_yaml="/etc/compass-ci/defaults/sparrow.yaml"
        mkdir -p $(dirname $config_yaml)

        cat >> $config_yaml <<-EOF
	SCHED_HOST: ${SCHED_HOST}
	SCHED_PORT: ${SCHED_PORT}
	my_account: ${my_account}
	my_name: ${secrets_MY_NAME}
	my_email: ${secrets_MY_EMAIL}
	my_token: ${secrets_MY_TOKEN}
	lab: ${lab}
	EOF
}

generate_submit_append_str()
{
	[ "$#" -eq 2 ] || die "generate_submit_append_str: please give 2 parameters."
	local os_version
	os_version="${iso_version}"
	[ -n "${dailybuild_iso_url_file}" ] && os_version="${iso_version}-dailybuild"

	echo "os=${iso_os} os_arch=${iso_arch} os_version=${os_version} os_mount=$1 testbox=$2 group_id=iso2rootfs-${iso_os}-${iso_arch}-${iso_version}-${ROOTFS_DES_DIR_SUFFIX}"
}

prepare_one_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/"
}

generate_initramfs()
{
	[ -n "$IS_INITRAMFS_GENERATED" ] && RETURN 0

	mount_initramfs

	local initramfs_cgz
	local os_version
	os_version="${iso_version}"
	[ -n "${dailybuild_iso_url_file}" ] && os_version="${iso_version}-dailybuild"
	initramfs_cgz=${INITRAMFS_LOCAL_PATH}/${os_version}/${ROOTFS_DES_DIR_SUFFIX}.cgz
	initramfs_dir=$(dirname $initramfs_cgz)
	[ -d "$initramfs_dir" ] || {
		mkdir -p $initramfs_dir
		chmod go+rx $initramfs_dir
	}

	[ -f "$initramfs_cgz" ] && return 0

	local rootfs_tmp
	rootfs_tmp=${ROOTFS_DES_DIR}-tmp
	cd ${ROOTFS_DES_DIR} && {
		mkdir -p ${rootfs_tmp}/modules
		mv lib/modules/* ${rootfs_tmp}/modules/

		mkdir -p ${rootfs_tmp}/boot
		mv boot/vmlinuz* ${rootfs_tmp}/boot/
		mv boot/initr* ${rootfs_tmp}/boot/

		find . | cpio -o -Hnewc | gzip -9 > $initramfs_cgz

		mv ${rootfs_tmp}/modules/* ./lib/modules/
		mv ${rootfs_tmp}/boot/* ./boot/

		rm -rf ${rootfs_tmp}
	}

	cd $initramfs_dir && {
		ln -sf $(basename $initramfs_cgz) current

		run_ipconfig_cgz="../../../../deps/nfs/debian/$os_arch/sid/run-ipconfig.cgz"
		ln -sf $run_ipconfig_cgz run-ipconfig.cgz
	}

	IS_INITRAMFS_GENERATED="yes"
}

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

	local test_git_url="$(eval echo "\$${test_num}_git_url")"
	[ -z "${test_git_url}" ] || prepare_one_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."

	[ "${test_os_mount}" == "initramfs" ] && generate_initramfs

	"${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

	prepare_lkp_client

	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_soft_link()
{
	local soft_link="${iso_version}"

	[ -n "${dailybuild_iso_url_file}" ] && {
		soft_link="${iso_version}-dailybuild"

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

	cd "${ROOTFS_LOCAL_PATH}" &&
		ln -sfT "$(basename "${ROOTFS_DES_DIR}")" "${soft_link}"
}

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

	update_soft_link

	submit_yamls

	cd / && umount "${ROOTFS_LOCAL_PATH}" "${INITRAMFS_LOCAL_PATH}"

	log_info "iso2rootfs finished"
}

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

	config_iso2qcow2
	run_iso2qcow2

	config_qcow2rootfs
	run_qcow2rootfs

	config_rootfs

	post_works
}

main
