# NoCloud Instance Metadata
# vim:set filetype=sh:
# shellcheck shell=sh

. "$LIBDIR/tiny-cloud/document"
. "$LIBDIR/tiny-cloud/imds-ec2"
. "$LIBDIR/tiny-cloud/imds-network-ec2"

NOCLOUD_FILES="meta-data user-data vendor-data network-config"
IMDS_NETWORK_INTERFACES="meta-data/network-interfaces"
: "${IMDS_NAMESERVERS:=meta-data/resolv_conf/nameservers}"

_load_nocloud_cmdline() {
	local options kv k v data loaded seedfrom source tmpfile

	options=$(nocloud_ds_options) || return
	set -- $options
	shift
	for kv; do
		k="${kv%%=*}"
		v="${kv#*=}"
		case "$k" in
			h|local-hostname)
				printf "\nlocal-hostname: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data"
				loaded=1
				;;
			i|instance-id)
				printf "\ninstance-id: %s" "$v" >> "$TINY_CLOUD_VAR/meta-data"
				loaded=1
				;;
			s|seedfrom)
				v=$(expand_nocloud_dmi_seedfrom "$v") || return 1
				seedfrom="${v%/}"
				for data in $NOCLOUD_FILES; do
					case "${v#file:/}" in
						/*)
							source="${seedfrom#file:}/$data"
							[ -f "$source" ] || continue
							cat "$source" >> "$TINY_CLOUD_VAR/$data" || continue
							echo >> "$TINY_CLOUD_VAR/$data"
							loaded=1
							;;
						http://*|https://*)
							tmpfile=$(mktemp "$TINY_CLOUD_VAR/$data.XXXXXX") ||
								continue
							wget -qO - "$seedfrom/$data" > "$tmpfile" || {
								rm -f "$tmpfile"
								continue
							}
							echo >> "$tmpfile" &&
								mv "$tmpfile" "$TINY_CLOUD_VAR/$data" || {
								rm -f "$tmpfile"
								continue
							}
							loaded=1
							;;
						*) log warning -s "Unknown NoCloud seedfrom value '$v'"
							;;
					esac
				done
				;;
			*)  log warning -s "Unknown NoCloud kernel cmdline key '$k'"
				;;
		esac
	done
	[ -n "$loaded" ]
}

_load_nocloud_volume() (
	local mntdir stagedir data mounted loaded

	. "$LIBDIR/tiny-cloud/metadata-volume"
	mntdir=$(mktemp -d "$TINY_CLOUD_RUN/metadata-volume.XXXXXX") || return
	trap '
		[ -z "$stagedir" ] || rm -rf "$stagedir"
		if [ -n "$mounted" ]; then
			metadata_volume_umount "$mntdir" || :
		else
			rmdir "$mntdir" 2>/dev/null || :
		fi
	' 0
	trap 'exit 1' HUP INT TERM

	metadata_volume_mount "$mntdir" "cidata CIDATA" "vfat iso9660" || return
	mounted=1
	stagedir=$(mktemp -d "$TINY_CLOUD_VAR/.nocloud.XXXXXX") || return
	for data in $NOCLOUD_FILES; do
		[ -f "$mntdir/$data" ] || continue
		cp "$mntdir/$data" "$stagedir/$data" || return
		loaded=1
	done
	[ -n "$loaded" ] || return 1

	metadata_volume_umount "$mntdir" || return
	mounted=
	for data in $NOCLOUD_FILES; do
		[ -f "$stagedir/$data" ] || continue
		mv "$stagedir/$data" "$TINY_CLOUD_VAR/$data" || return
	done
)

load_nocloud() {
	# start with a clean slate
	(cd "$TINY_CLOUD_VAR" && rm -f $NOCLOUD_FILES)

	if _load_nocloud_cmdline || _load_nocloud_volume; then
		touch "$TINY_CLOUD_VAR/.nocloud_loaded"
	else
		log err -s "Unable to load NoCloud datasource"
		return 1
	fi

	# minimally, we expect some content in meta-data
	[ -s "$TINY_CLOUD_VAR/meta-data" ] ||
		log warning -s "NoCloud 'meta-data' is empty"
}

_imds() {
	local path="$1" document="${1%%/*}" file

	[ -f "$TINY_CLOUD_VAR/.nocloud_loaded" ] || load_nocloud || return
	file="$TINY_CLOUD_VAR/$document"
	[ -f "$file" ] || return 1

	if [ "$path" = "$document" ]; then
		cat "$file"
	else
		document_select "$file" "${path#*/}" 2>/dev/null
	fi
}

_imds_nameservers_metadata() {
	local indexes index server

	indexes="$(_imds "$IMDS_NAMESERVERS")" || return
	for index in $indexes; do
		server="$(_imds "$IMDS_NAMESERVERS/$index")" || continue
		printf '%s\n' "$server"
	done
}

_imds_nic_value() {
	local path="$IMDS_NICS/$1${3:+/$3}" value index address
	case "$2" in
		@mac|@ipv4|@ipv6|@ipv4-net|@ipv6-net|@ipv4-prefix|@ipv6-prefix)
			value=$(_imds "$path") || return 1
			;;
		*) _imds "$path"; return $?;;
	esac
	case "$value" in ''|null) return 0;; esac
	case "$2" in
		@mac) printf '%s\n' "$value" | lower; return;;
	esac
	# yx lists sequence indexes; scalar addresses and CIDRs contain punctuation.
	case "$value" in
		*[!0-9[:space:]]*) printf '%s\n' "$value";;
		*)
			for index in $value; do
				address=$(_imds "$path/$index") || return 1
				case "$address" in ''|null) continue;; esac
				printf '%s\n' "$address"
			done
			;;
	esac
}
