# Script UserData Functions
# vim:set filetype=sh:
# shellcheck shell=sh

INIT_ACTIONS_MAIN="$(insert_before create_default_user userdata_user $INIT_ACTIONS_MAIN)"
INIT_ACTIONS_MAIN="$(insert_after set_hostname \
	"userdata_bootcmd userdata_write_files userdata_ntp userdata_apk_cache userdata_apk_repositories userdata_package_update userdata_package_upgrade userdata_packages" \
	$INIT_ACTIONS_MAIN)"
INIT_ACTIONS_MAIN="$(insert_after set_ssh_keys ssh_authorized_keys $INIT_ACTIONS_MAIN)"
INIT_ACTIONS_FINAL="$INIT_ACTIONS_FINAL userdata_runcmd"

get_userdata() {
	IFS="/"
	yx -f "$TINY_CLOUD_VAR/user-data" $1 2>/dev/null
	unset IFS
}

init__userdata_user() {
	local name="$(get_userdata user/name)"
	if [ -z "$name" ]; then
		name="$(get_userdata user)"
		if [ -n "$(get_userdata user/$name)" ]; then
			log -s err "user/name is required"
			return
		fi
	fi
	CLOUD_USER="${name:-$CLOUD_USER}"
}

init__ssh_authorized_keys() {
	local sshkeys="$(get_userdata ssh_authorized_keys)"
	if [ -z "$sshkeys" ]; then
		return
	fi
	local user="$CLOUD_USER"
	local pwent="$(getent passwd "$user")"
	if [ -z "$pwent" ]; then
		log -i -t "$phase" err "$ACTION: failed to find user $user"
		return 1
	fi
	local group=$(echo "$pwent" | cut -d: -f4)
	local ssh_dir="${ROOT}$(echo "$pwent" | cut -d: -f6)/.ssh"
	local keys_file="$ssh_dir/authorized_keys"

	if [ ! -d "$ssh_dir" ]; then
		mkdir -p "$ssh_dir"
		chmod 700 "$ssh_dir"
	fi

	touch "$keys_file"
	chmod 600 "$keys_file"
	$MOCK chown -R "$user:$group" "$ssh_dir"
	for i in $sshkeys; do
		local key="$(get_userdata ssh_authorized_keys/$i)"
		if [ -n "$key" ]; then
			echo "$key" >> "$keys_file"
		fi
	done
}


init__userdata_bootcmd() {
	# run bootcmd
	local bootcmds="$(get_userdata bootcmd)"
	for i in $bootcmds; do
		local cmd="$(get_userdata bootcmd/"$i")"
		sh -c "$cmd"
	done
}

# write_file <path> <mode> <owner> <encoding> <append>
write_file() {
	# Defaults used are the same as for full cloud-init "spec":
	# https://cloudinit.readthedocs.io/en/latest/reference/modules.html#write-files
	local path="$1"
	local mode="${2:-0644}"
	local owner="${3:-root:root}"
	local encoding="${4:-text/plain}"
	local append="${5:-false}"

	if [ "$append" != "true" ] && [ "$append" != "false" ]; then
		log err "append must be true or false"
		return
	fi

	local tmpfile="$(mktemp $TINY_CLOUD_VAR/user-data.write_files.XXXXXX)"

	case "$encoding" in
		gzip|gz|gz+base64|gzip+base64|gz+b64|gzip+b64)
			base64 -d | gzip -d > "$tmpfile"
			;;
		base64|b64)
			base64 -d > "$tmpfile"
			;;
		text/plain)
			cat > "$tmpfile"
			;;
	esac

	if [ "$append" = "true" ]; then
		cat "$tmpfile" >> "$path"
	else
		cat "$tmpfile" > "$path"
	fi
	rm -f "$tmpfile"

	chmod "$mode" "$path"
	# mocked as we do not know which users we could use in testing
	# this way we can check the proper invocation at least
	$MOCK chown "$owner" "$path"
}

init__userdata_write_files() {
	local files="$(get_userdata write_files)"

	for i in $files; do
		local path="$(get_userdata write_files/$i/path)"
		if [ -z "$path" ]; then
			continue
		fi

		mkdir -p "$(dirname "$ROOT/$path")"
		get_userdata write_files/$i/content | write_file "$ROOT/$path" \
			"$(get_userdata write_files/$i/permissions)" \
			"$(get_userdata write_files/$i/owner)" \
			"$(get_userdata write_files/$i/encoding)" \
			"$(get_userdata write_files/$i/append)"
	done
}

init__userdata_ntp() {
	local ntp_enabled="$(get_userdata ntp/enabled)"
	if [ "$ntp_enabled" != "yes" ] && [ "$ntp_enabled" != "true" ]; then
		return
	fi
	local ntp_client="$(get_userdata ntp/ntp_client)"
	local svc= pkg=
	case "$ntp_client" in
		busybox)
			svc=ntpd
			;;
		chrony|"")
			pkg=chrony
			svc=chronyd
			;;
		openntpd)
			pkg=openntpd
			svc=openntpd
			;;
	esac
	if [ -n "$pkg" ]; then
		$MOCK apk add "$pkg"
	fi
	if [ -n "$svc" ]; then
		$MOCK rc-update add "$svc" default
		$MOCK rc-service "$svc" start
	fi
}

init__userdata_apk_cache() {
	local cache="$(get_userdata apk/cache)"
	if [ -z "$cache" ]; then
		return
	fi
	mkdir -p "$ROOT/$cache"
	# make link relative
	case "$cache" in
		/*) cache="../..$cache";;
	esac
	mkdir -p "$ROOT"/etc/apk
	ln -sf "$cache" "$ROOT"/etc/apk/cache
}

init__userdata_apk_repositories() {
	local repositories="$(get_userdata apk/repositories)"
	mkdir -p "$ROOT"/etc/apk
	for r in $repositories; do
		local baseurl="$(get_userdata apk/repositories/$r/base_url)"
		local repos="$(get_userdata apk/repositories/$r/repos)"
		local version="$(get_userdata apk/repositories/$r/version)"
		if [ -z "$version" ]; then
			local version_id=$( . "$ROOT"/etc/os-release 2>/dev/null && echo "$VERSION_ID")
			case "$version_id" in
				edge*|*_alpha*) version="edge";;
				[0-9]*.[0-9]*.[0-9]*) version="v${version_id%.*}";;
			esac
		fi
		if [ -n "$version" ] && [ "$version" != "." ] && [ "$version" != "/" ]; then
			baseurl="${baseurl%/}/$version"
		fi
		for repo in $repos; do
			local uri="${baseurl%/}/$(get_userdata apk/repositories/$r/repos/$repo)"
			add_once "$ROOT"/etc/apk/repositories "$uri"
		done
	done
}

init__userdata_package_update() {
	local update="$(get_userdata package_update)"
	if [ "$update" = "true" ]; then
		$MOCK apk update
	fi
}

init__userdata_package_upgrade() {
	local upgrade="$(get_userdata package_upgrade)"
	if [ "$upgrade" = "true" ]; then
		$MOCK apk upgrade
	fi
}

init__userdata_packages() {
	local packages="$(get_userdata packages)"
	local pkgs=
	for i in $packages; do
		pkgs="$pkgs $(get_userdata packages/$i)"
	done
	if [ -n "$pkgs" ]; then
		$MOCK apk add $pkgs
	fi
}

init__userdata_runcmd() {
	local runcmds="$(get_userdata runcmd)"
	for i in $runcmds; do
		local cmd="$(get_userdata runcmd/$i)"
		sh -c "$cmd"
	done
}
