# Google Cloud Instance MetaData Service variables and functions
# vim:set filetype=sh:
# shellcheck shell=sh

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

: "${IMDS_API_VERSION:=v1}"
IMDS_URI="computeMetadata/$IMDS_API_VERSION"
IMDS_HEADER="Metadata-Flavor"

IMDS_HOSTNAME="instance/hostname"
IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME"
IMDS_SSH_KEYS="
	project/attributes/ssh-keys
	instance/attributes/ssh-keys
"
IMDS_USERDATA="instance/attributes/user-data"
IMDS_NICS="instance/network-interfaces"
IMDS_MAC="mac"
IMDS_IPV4="ip"
IMDS_IPV6="ipv6s"
IMDS_IPV4_NET="subnetmask"
IMDS_IPV4_PREFIX="ipAliases"
IMDS_IPV6_PREFIX="ipv6"

: "${DEFAULT_NAMESERVERS=169.254.169.254 fd20:ce::254}"

_imds_nic_data() {
	local IMDS_QUERY="?recursive=true"
	_imds_fetch_cached "$IMDS_CACHE_DIR/nics" _imds "$IMDS_NICS/" || return
	cat "$IMDS_CACHE_DIR/nics"
}

# Print an optional NIC field, expanding arrays one value per line.
_imds_nic_values() {
	local data="$1" nic="$2" field="$3"
	local keys indexes index value

	keys=$(printf '%s' "$data" | yx -- "$nic") || return 1
	printf '%s\n' "$keys" | grep -qxF "$field" || return 0
	value=$(printf '%s' "$data" | yx -- "$nic" "$field") || return 1
	case "$value" in ''|null) return 0;; esac
	case "$field" in
		mac|ip|subnetmask) printf '%s\n' "$value"; return 0;;
	esac
	indexes="$value"
	for index in $indexes; do
		value=$(printf '%s' "$data" |
			yx -- "$nic" "$field" "$index") || return 1
		case "$value" in ''|null) continue;; esac
		printf '%s\n' "$value"
	done
}

_imds_init() { _imds_cache_init; }

_imds_header() {
	echo "$IMDS_HEADER: Google"
}

# merge project and instance keys
_imds_ssh_keys() {
	local ssh_keys

	# NOTE: keys are prefixed with "<login>:"
	for ssh_keys in $IMDS_SSH_KEYS; do
		imds -e "$ssh_keys"
	done | sort -u
}

_imds_nameservers_metadata() {
	local data indexes index values result=

	data=$(_imds_nic_data) || return 1
	indexes=$(printf '%s' "$data" | yx) || return 1
	for index in $indexes; do
		values=$(_imds_nic_values "$data" "$index" dnsServers) || return 1
		[ -z "$values" ] || result="${result}${values}
"
	done
	[ -z "$result" ] || printf '%s' "$result" | sort -u
}

_imds_nic_index() {
	local mac data indexes index address

	mac=$(cat "$SYS/class/net/$1/address") || return 1
	mac=$(printf '%s' "$mac" | lower)
	[ -n "$mac" ] || return 1

	data=$(_imds_nic_data) || return 1
	indexes=$(printf '%s' "$data" | yx) || return 1
	for index in $indexes; do
		address=$(printf '%s' "$data" | yx -- "$index" mac) || return 1
		address=$(printf '%s' "$address" | lower)
		[ "$address" = "$mac" ] || continue
		echo "$((index - 1))"
		return 0
	done
	return 1
}

_imds_nic_value() {
	local data nic="$(( $1 + 1 ))" address mask

	case "$2" in
		@mac|@ipv4|@ipv6|@ipv4-net|@ipv4-prefix|@ipv6-prefix)
			data=$(_imds_nic_data) || return 1
			;;
		*)
			_imds "$IMDS_NICS/$1/$3"
			return $?
			;;
	esac
	case "$2" in
		@mac)
			address=$(_imds_nic_values "$data" "$nic" "$3") || return 1
			[ -z "$address" ] || printf '%s\n' "$address" | lower
			;;
		@ipv4|@ipv6|@ipv4-prefix|@ipv6-prefix)
			_imds_nic_values "$data" "$nic" "$3"
			;;
		@ipv4-net)
			address=$(_imds_nic_values "$data" "$nic" ip) || return 1
			[ -n "$address" ] || return 0
			mask=$(_imds_nic_values "$data" "$nic" subnetmask) || return 1
			[ -n "$mask" ] || return 1
			_imds_ipv4_network "$address" "$mask"
			;;
	esac
}
