#!/bin/bash
set -e

# Logging helper functions
info()
{
    echo "INFO:" "$@" 1>&2
}

error()
{
    echo "ERROR:" "$@" 1>&2
}
warn()
{
    echo "WARN:" "$@" 1>&2
}

# Print all given arguments
if [ $# -ne 0 ]; then
    info "Arguments: $(echo $@ | sed -e 's/\(token\s\)\w*/\1REDACTED/')"
fi

if [ "$1" = "--" ]; then
    shift 1
    exec "$@"
fi

if [ "$CLUSTER_CLEANUP" = true ]; then
    exec agent
fi

get_address()
{
    local address=$1
    # If nothing is given, return empty (it will be automatically determined later if empty)
    if [ -z $address ]; then
        echo ""
    # If given address is a network interface on the system, retrieve configured IP on that interface (only the first configured IP is taken)
    elif [ -n "$(find /sys/devices -name $address)" ]; then
        echo $(ip addr show dev $address | grep -w inet | awk '{print $2}' | cut -f1 -d/ | head -1)
    # Loop through cloud provider options to get IP from metadata, if not found return given value
    else
        case $address in
            awslocal)
                echo $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)
                ;;
            awspublic)
                echo $(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)
                ;;
            doprivate)
                echo $(curl -s http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address)
                ;;
            dopublic)
                echo $(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
                ;;
            azprivate)
                echo $(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2017-08-01&format=text")
                ;;
            azpublic)
                echo $(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-08-01&format=text")
                ;;
            gceinternal)
                echo $(curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip)
                ;;
            gceexternal)
                echo $(curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip)
                ;;
            packetlocal)
                echo $(curl -s https://metadata.packet.net/2009-04-04/meta-data/local-ipv4)
                ;;
            packetpublic)
                echo $(curl -s https://metadata.packet.net/2009-04-04/meta-data/public-ipv4)
                ;;
            ipify)
                echo $(curl -s https://api.ipify.org)
                ;;
            *)
                echo $address
                ;;
        esac
    fi
}

check_url()
{
    local url=$1
    local err
    err=$(curl --insecure -sS -fL -o /dev/null --stderr - $url | head -n1 ; exit ${PIPESTATUS[0]})
    if [ $? -eq 0 ]
    then
        echo ""
    else
        echo ${err} | sed -e 's/^curl: ([0-9]\+) //'
    fi
}

check_x509_cert()
{
    local cert=$1
    local err
    err=$(openssl x509 -in $cert -noout 2>&1)
    if [ $? -eq 0 ]
    then
        echo ""
    else
        echo ${err}
    fi
}

export CATTLE_ADDRESS
export CATTLE_AGENT_CONNECT
export CATTLE_INTERNAL_ADDRESS
export CATTLE_NODE_NAME
export CATTLE_ROLE
export CATTLE_SERVER
export CATTLE_TOKEN
export CATTLE_NODE_LABEL
export CATTLE_WRITE_CERT_ONLY
export CATTLE_NODE_TAINTS

while true; do
    case "$1" in
        -d | --debug)                   DEBUG=true                  ;;
        -s | --server)           shift; CATTLE_SERVER=$1            ;;
        -t | --token)            shift; CATTLE_TOKEN=$1             ;;
        -c | --ca-checksum)      shift; CATTLE_CA_CHECKSUM=$1       ;;
        -a | --all-roles)               ALL=true                    ;;
        -e | --etcd)                    ETCD=true                   ;;
        -w | --worker)                  WORKER=true                 ;;
        -p | --controlplane)            CONTROL=true                ;;
        -n | --node-name)        shift; CATTLE_NODE_NAME=$1         ;;
        -r | --no-register)             CATTLE_AGENT_CONNECT=true   ;;
        -a | --address)          shift; CATTLE_ADDRESS=$1           ;;
        -i | --internal-address) shift; CATTLE_INTERNAL_ADDRESS=$1  ;;
        -l | --label)            shift; CATTLE_NODE_LABEL+=",$1"    ;;
        -o | --only-write-certs)        CATTLE_WRITE_CERT_ONLY=true ;;
        --taints)                shift; CATTLE_NODE_TAINTS+=",$1"   ;;
        *) break;
    esac
    shift
done

if [ "$DEBUG" = true ]; then
    set -x
fi

if [ "$CATTLE_CLUSTER" != "true" ]; then
    if [ ! -w /var/run/docker.sock ] || [ ! -S /var/run/docker.sock ]; then
        warn "Docker socket is not available!"
        warn "Please bind mount in the docker socket to /var/run/docker.sock if docker errors occur"
        warn "example:  docker run -v /var/run/docker.sock:/var/run/docker.sock ..."
    fi
fi

if [ -z "$CATTLE_NODE_NAME" ]; then
    CATTLE_NODE_NAME=$(hostname -s)
fi

export CATTLE_ADDRESS=$(get_address $CATTLE_ADDRESS)
export CATTLE_INTERNAL_ADDRESS=$(get_address $CATTLE_INTERNAL_ADDRESS)

if [ -z "$CATTLE_ADDRESS" ]; then
    # Example output: '8.8.8.8 via 10.128.0.1 dev ens4 src 10.128.0.34 uid 0'
    CATTLE_ADDRESS=$(ip -o route get 8.8.8.8 | sed -n 's/.*src \([0-9.]\+\).*/\1/p')
fi

if [ "$CATTLE_K8S_MANAGED" != "true" ]; then
    if [ -z "$CATTLE_TOKEN" ]; then
        error -- --token is a required option
        exit 1
    fi

    if [ -z "$CATTLE_ADDRESS" ]; then
        error -- --address is a required option
        exit 1
    fi
fi

if [ "$ALL" = true ]; then
    CATTLE_ROLE="etcd,worker,controlplane"
else
    if [ "$ETCD" = true ]; then
        CATTLE_ROLE="${CATTLE_ROLE},etcd"
    fi
    if [ "$WORKER" = true ]; then
        CATTLE_ROLE="${CATTLE_ROLE},worker"
    fi
    if [ "$CONTROL" = true ]; then
        CATTLE_ROLE="${CATTLE_ROLE},controlplane"
    fi
fi

if [ -z "$CATTLE_SERVER" ]; then
    error -- --server is a required option
    exit 1
fi

info "Environment: $(echo $(printenv | grep CATTLE | sort | sed -e 's/\(CATTLE_TOKEN=\).*/\1REDACTED/'))"
info "Using resolv.conf: $(echo $(cat /etc/resolv.conf | grep -v ^#))"
if grep -E -q '^nameserver 127.*.*.*|^nameserver localhost|^nameserver ::1' /etc/resolv.conf; then
    warn "Loopback address found in /etc/resolv.conf, please refer to the documentation how to configure your cluster to resolve DNS properly"
fi

CATTLE_SERVER_PING="${CATTLE_SERVER}/ping"
err=$(check_url $CATTLE_SERVER_PING)
if [[ $err ]]; then
    error "${CATTLE_SERVER_PING} is not accessible (${err})"
    exit 1
else
    info "${CATTLE_SERVER_PING} is accessible"
fi

# Extract hostname from URL
CATTLE_SERVER_HOSTNAME=$(echo $CATTLE_SERVER | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')
CATTLE_SERVER_HOSTNAME_WITH_PORT=$(echo $CATTLE_SERVER | sed -e 's/[^/]*\/\/\([^@]*@\)\?\(.*\).*/\2/')
# Resolve IPv4 address(es) from hostname
RESOLVED_ADDR=$(getent ahostsv4 $CATTLE_SERVER_HOSTNAME | sed -n 's/ *STREAM.*//p')

# If CATTLE_SERVER_HOSTNAME equals RESOLVED_ADDR, its an IP address and there is nothing to resolve
if [ "${CATTLE_SERVER_HOSTNAME}" != "${RESOLVED_ADDR}" ]; then
  info "${CATTLE_SERVER_HOSTNAME} resolves to $(echo $RESOLVED_ADDR)"
fi

if [ -n "$CATTLE_CA_CHECKSUM" ]; then
    temp=$(mktemp)
    curl --insecure -s -fL $CATTLE_SERVER/v3/settings/cacerts | jq -r '.value | select(length > 0)' > $temp
    if [ ! -s $temp ]; then
      error "The environment variable CATTLE_CA_CHECKSUM is set but there is no CA certificate configured at $CATTLE_SERVER/v3/settings/cacerts"
      exit 1
    fi
    err=$(check_x509_cert $temp)
    if [[ $err ]]; then
        error "Value from $CATTLE_SERVER/v3/settings/cacerts does not look like an x509 certificate (${err})"
        error "Retrieved cacerts:"
        cat $temp
        exit 1
    else
        info "Value from $CATTLE_SERVER/v3/settings/cacerts is an x509 certificate"
    fi
    CATTLE_SERVER_CHECKSUM=$(sha256sum $temp | awk '{print $1}')
    if [ $CATTLE_SERVER_CHECKSUM != $CATTLE_CA_CHECKSUM ]; then
        rm -f $temp
        error "Configured cacerts checksum ($CATTLE_SERVER_CHECKSUM) does not match given --ca-checksum ($CATTLE_CA_CHECKSUM)"
        error "Please check if the correct certificate is configured at $CATTLE_SERVER/v3/settings/cacerts"
        exit 1
    else
        mkdir -p /etc/kubernetes/ssl/certs
        mv $temp /etc/kubernetes/ssl/certs/serverca
        chmod 755 /etc/kubernetes/ssl
        chmod 700 /etc/kubernetes/ssl/certs
        chmod 600 /etc/kubernetes/ssl/certs/serverca
        mkdir -p /etc/docker/certs.d/$CATTLE_SERVER_HOSTNAME_WITH_PORT
        cp /etc/kubernetes/ssl/certs/serverca /etc/docker/certs.d/$CATTLE_SERVER_HOSTNAME_WITH_PORT/ca.crt

    fi
fi

exec tini -- agent

