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

: "${IMDS_API_VERSION:=v2}"
IMDS_URI="opc/$IMDS_API_VERSION"
IMDS_HEADER="Authorization"

IMDS_HOSTNAME="instance/hostname"
IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME"
IMDS_SSH_KEYS="instance/metadata/ssh_authorized_keys"
IMDS_USERDATA="instance/metadata/user_data"
IMDS_NICS="vnics"
IMDS_MAC="macAddr"
IMDS_IPV4="privateIp"
IMDS_IPV6="ipv6Addresses"
IMDS_IPV4_NET="subnetCidrBlocks"
IMDS_IPV6_NET="ipv6SubnetCidrBlocks"

: "${DEFAULT_NAMESERVERS=169.254.169.254}"

_imds_nic_data() {
	local file="$IMDS_CACHE_DIR/vnics${1:+-$1}"
	_imds_fetch_cached "$file" _imds "$IMDS_NICS/${1:+$1/}" || return
	cat "$file"
}

# Print scalar or array values; absent optional fields produce no output.
_imds_nic_values() {
	local data="$1" field="$2" fallback="$3"
	local keys value indexes index

	keys=$(printf '%s' "$data" | yx) || return 1
	if ! printf '%s\n' "$keys" | grep -qxF "$field"; then
		[ -n "$fallback" ] || return 0
		field="$fallback"
		printf '%s\n' "$keys" | grep -qxF "$field" || return 0
	fi

	value=$(printf '%s' "$data" | yx -- "$field") || return 1
	case "$value" in
		''|null) return 0;;
	esac

	case "$field" in
		secondaryPrivateIps|ipv6Addresses|subnetCidrBlocks|ipv6SubnetCidrBlocks)
			indexes="$value"
			for index in $indexes; do
				value=$(printf '%s' "$data" |
					yx -- "$field" "$index") || return 1
				case "$value" in
					''|null) continue;;
				esac
				printf '%s\n' "$value"
			done
			;;
		*) printf '%s\n' "$value";;
	esac
}

_imds_init() { _imds_cache_init; }

_imds_header() {
	echo "$IMDS_HEADER: Bearer Oracle"
}

_imds_ssh_keys() { _imds "$IMDS_SSH_KEYS"; }

# decode userdata value
_imds_userdata() {
	imds "$IMDS_USERDATA" | base64 -d
}

_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" macAddr) || return 1
		address=$(printf '%s' "$address" | lower)
		[ "$address" = "$mac" ] || continue
		# yx sequence indexes start at 1; OCI indexes start at 0.
		echo "$((index - 1))"
		return 0
	done
	return 1
}

_imds_nic_value() {
	local path="$IMDS_NICS/$1" data address

	case "$2" in
		@mac|@ipv4|@ipv6|@ipv4-net|@ipv6-net)
			data=$(_imds_nic_data "$1") || return 1
			;;
		*)
			_imds "$path/$3"
			return $?
			;;
	esac

	case "$2" in
		@mac)
			address=$(_imds_nic_values "$data" "$3") || return 1
			[ -z "$address" ] || printf '%s\n' "$address" | lower
			;;
		@ipv4)
			_imds_nic_values "$data" "$3" || return 1
			_imds_nic_values "$data" secondaryPrivateIps
			;;
		@ipv6) _imds_nic_values "$data" "$3";;
		@ipv4-net) _imds_nic_values "$data" "$3" subnetCidrBlock;;
		@ipv6-net) _imds_nic_values "$data" "$3" ipv6SubnetCidrBlock;;
	esac
}
