# OpenStack metadata source selection
# vim:set filetype=sh:
# shellcheck shell=sh

# Return 1 for an absent/unusable drive, 2 for a failed load.
_load_openstack_volume() (
	local mntdir stagedir mounted format data metadata value loaded rv
	local version="${IMDS_API_VERSION:-latest}"

	findfs LABEL=config-2 >/dev/null 2>&1 ||
		findfs LABEL=CONFIG-2 >/dev/null 2>&1 || return 1

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

	for format in native ec2; do
		case "${OPENSTACK_IMDS_FORMAT:-auto}" in
			auto|"$format") ;;
			*) continue;;
		esac
		case "$format" in
			native)
				case "$version" in
					''|.*|*[!A-Za-z0-9_.-]*) continue;;
				esac
				data="$mntdir/openstack/$version"
				metadata=meta_data.json
				value=$(yx -f "$data/$metadata" -- uuid 2>/dev/null) || continue
				;;
			ec2)
				data="$mntdir/ec2/latest"
				metadata=meta-data.json
				value=$(yx -f "$data/$metadata" -- instance-id 2>/dev/null) || continue
				;;
		esac
		case "$value" in ''|null) continue;; esac
		loaded=1
		break
	done
	[ -n "$loaded" ] || return 1

	stagedir=$(mktemp -d "$TINY_CLOUD_RUN/openstack-volume.XXXXXX") || return 2
	case "$format" in
		native) set -- meta_data.json user_data network_data.json vendor_data.json vendor_data2.json;;
		ec2) set -- meta-data.json user-data;;
	esac
	for metadata; do
		[ -f "$data/$metadata" ] || continue
		cp "$data/$metadata" "$stagedir/$metadata" || return 2
	done
	printf '%s\n' "$format" > "$stagedir/format" || return 2
	metadata_volume_umount "$mntdir" || return 2
	mounted=
	mv "$stagedir" "$TINY_CLOUD_RUN/openstack-configdrive" || return 2
	stagedir=
)

_openstack_select_source() (
	local source rv attempts=0 tmpfile
	local marker="$TINY_CLOUD_RUN/openstack-source"

	exec 8>"$TINY_CLOUD_RUN/openstack-source.lock" || return
	while ! flock -n 8; do
		attempts=$((attempts + 1))
		if [ "$attempts" -ge 300 ]; then
			log err -s "Timed out waiting for OpenStack source selection"
			return 1
		fi
		sleep 0.1
	done
	if [ -f "$marker" ]; then
		source=$(cat "$marker") || return
	else
		# A previous interrupted selection may have copied the drive already.
		if [ -f "$TINY_CLOUD_RUN/openstack-configdrive/format" ]; then
			source=configdrive
		elif _load_openstack_volume; then
			source=configdrive
		else
			rv=$?
			[ "$rv" -eq 1 ] || return "$rv"
			source=http
		fi
		tmpfile=$(mktemp "$marker.XXXXXX") || return
		printf '%s\n' "$source" > "$tmpfile" && mv "$tmpfile" "$marker" || {
			rm -f "$tmpfile"
			return 1
		}
	fi
	case "$source" in
		http|configdrive) printf '%s\n' "$source";;
		*) log err -s "Invalid saved OpenStack source"; return 1;;
	esac
)

# Disk metadata needs no bootstrap network; HTTP retains endpoint overrides.
_imds_targets() {
	local source
	source=$(_openstack_select_source) || return
	[ "$source" = http ] || return 0
	printf '%s\n' "$IMDS_ENDPOINTS"
}
