#!/bin/sh
# vim:set ts=4 et ft=sh:

# Tiny Cloud - Instance MetaData Service client

### configuration, common functions

: "${LIBDIR:=$PREFIX/lib}"
. "$LIBDIR"/tiny-cloud/common

if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    cat <<EOT
Usage: imds [-h] { -e | +e | +n | +s | +t | @<alias> | <imds-path> } ...
  -h           : help
  -e / +e      : ignore / catch errors
  +n / +s / +t : insert newline / space / tab
  <alias> :-
    hostname : instance hostname
    ssh-keys : instance SSH keys
    userdata : instance user data
    nics     : instance NICs
    nic:<iface>[,<nic-key> ...] : specific NIC interface
      <iface>   : network interface (i.e. eth1)
      <nic-key> :- { -e | +e | +n | +s | +t | @<nic-alias> | <nic-path> }
        <nic-alias> :-
          mac         : mac address
          ipv4        : ipv4 address(es)
          ipv6        : ipv6 address(es)
          ipv4-net    : subnet ipv4 network(s)
          ipv6-net    : subnet ipv6 network(s)
          ipv4-prefix : delegated ipv4 CIDR(s)
          ipv6-prefix : delegated ipv6 CIDR(s)
EOT
    exit 0
fi

### cloud-specific variables/functions

unset \
    IMDS_HEADER \
    IMDS_URI \
    IMDS_QUERY
unset -f \
    _imds_token \
    _imds_header \
    _imds_nic_index \
        2>/dev/null	|| true

### default variables/functions

# Common to many clouds
IMDS_ENDPOINT="169.254.169.254"

# Common to AWS and NoCloud(ish)
IMDS_HOSTNAME="meta-data/hostname"
IMDS_SSH_KEYS="meta-data/public-keys"
IMDS_USERDATA="user-data"
IMDS_NICS="meta-data/network/interfaces/macs"
IMDS_MAC="mac"
IMDS_IPV4="local-ipv4s"
IMDS_IPV6="ipv6s"
IMDS_IPV4_NET="subnet-ipv4-cidr-block"
IMDS_IPV6_NET="subnet-ipv6-cidr-blocks"
IMDS_IPV4_PREFIX="ipv4-prefix"
IMDS_IPV6_PREFIX="ipv6-prefix"

_imds() {
    wget --quiet --timeout 1 --output-document - \
        --header "$(_imds_header)" \
        "http://$IMDS_ENDPOINT/$IMDS_URI/$1$IMDS_QUERY"
}

_imds_userdata() { _imds "$IMDS_USERDATA"; }

_imds_ssh_keys() {
    local key
    for key in $(_imds "$IMDS_SSH_KEYS"); do
        _imds "$IMDS_SSH_KEYS/${key%=*}/openssh-key"
        echo
    done | sort -u
}

_imds_nic_index() { cat "/sys/class/net/$1/address"; }

### load cloud-specific variables and functions

if [ ! -d "$LIBDIR"/tiny-cloud/"$CLOUD" ]; then
    echo "ERROR: Unknown Cloud '$CLOUD'" >&2
fi
. "$LIBDIR"/tiny-cloud/"$CLOUD"/imds

### non-overrideable functions

imds() {
    local cmd args key rv err=1
    while [ -n "$1" ]; do
        cmd=_imds
        args=
        key="$1"; shift
        case $key in
            # error handling
            -e) err=0; continue ;;          # ignore
            +e) err=1; continue ;;          # return
            # TODO: retry/deadline
            # output control
            +n) printf "\n"; continue ;;           # insert newline
            +s) printf " "; continue ;;    # insert space
            +t) printf "\t"; continue ;;  # insert tab
            # key aliasing
            @hostname)  args="$IMDS_HOSTNAME" ;;
            @ssh-keys)  cmd=_imds_ssh_keys ;;
            @userdata)  cmd=_imds_userdata ;;
            @nics)      args="$IMDS_NICS" ;;
            @nic:*)
                cmd=imds
                args=$(_imds_nic_args $(echo "${key#@nic:}" | tr , ' '))
                ;;
            # use key verbatim
            *) args="$key" ;;
        esac
        # TODO: retry/deadline
        "$cmd" $args 2>/dev/null
        rv=$?
        [ $err -eq 0 ] && continue
        [ $rv = "0" ] || return $rv
    done
}

_imds_nic_args() {
    local key nic
    nic=$(_imds_nic_index "$1") || return 1
    if [ -z "$2" ]; then
        echo "$IMDS_NICS/$nic"
        return
    fi
    while [ -n "$2" ]; do
        key="$2"
        shift
        case "$key" in
            @mac)           key="$IMDS_MAC" ;;
            @ipv4)          key="$IMDS_IPV4" ;;
            @ipv6)          key="$IMDS_IPV6" ;;
            @ipv4-net)      key="$IMDS_IPV4_NET" ;;
            @ipv6-net)      key="$IMDS_IPV6_NET" ;;
            @ipv4-prefix)   key="$IMDS_IPV4_PREFIX" ;;
            @ipv6-prefix)   key="$IMDS_IPV6_PREFIX" ;;
            # error/output control passthrough
            -e|+[enst])     printf "$key\n"; continue ;;
        esac
        printf "$IMDS_NICS/$nic/$key\n"
    done
}

imds "$@"
