#!/bin/bash

set -e

ARGS=$@

EB_PROFILE=$(klue_config --aws-user)
EB_TIMEOUT=15
EB_INSTANCE_TYPE=$(klue_config --aws-instance-type)
EB_PLATFORM=docker-1.12.6
AWS_KEYNAME=$(klue_config --aws-keypair)
ARN_CERT=$(klue_config --aws-cert-arn)
DOCKER_BUCKET=$(klue_config --docker-bucket)

AUTO_MINSIZE=$(klue_config --aws-hosts-min)
AUTO_MAXSIZE=$(klue_config --aws-hosts-max)

DEPLOY_ONLY=0
VERSION=
PROG=$(basename $0)


cleanup() {
    echo "=> Removing $TMPDIR"
    rm -rf $TMPDIR
}

usage() {
    cat << EOF
USAGE: $PROG [--with-downtime] <version>

Create a new elasticbeanstalk environment running the current source code in a
docker container, labelled with the given version number. Perform the steps:

OPTIONS:
  --with-downtime:  Deploy to current environment instead of creating a new one
                    then swapping. This implies downtime.

EOF
}

parse_args() {
    while [ "$1" != "" ]; do
        case $1 in
            "--debug")         set -x; DEBUG='true';;
            "--with-downtime") export DEPLOY_ONLY=1;;
            "-h" | "--help")   usage; exit 0;;
            *)                 VERSION=$1;;
        esac
        shift
    done
}

parse_args $ARGS

if [ -z "$VERSION" ]; then
    usage
    exit 1
fi

IS_DIRTY_CLONE=$(git status --short --porcelain | wc -l)
if [ "$IS_DIRTY_CLONE" -gt 0 ]; then
    echo "ERROR: this clone is not clean! Commit and re-run."
#    exit 1
fi

ROOTDIR=$(git rev-parse --show-toplevel)
if [ "$PWD" != "$ROOTDIR" ]; then
    echo "ERROR: current dir is not the clone's root directory"
    exit 1
fi

DOCKERRUN_TEMPLATE=$(get_deploy_config_path)/Dockerrun.aws.json.template
if [ ! -e "$DOCKERRUN_TEMPLATE" ]; then
    echo "ERROR: cannot find $DOCKERRUN_TEMPLATE"
    exit 1
fi

PROJECT_NAME=$(klue_config --name)
DOCKER_ROOT_REPO=$(klue_config --docker-repo)

DOCKER_REPO=$DOCKER_ROOT_REPO/$PROJECT_NAME

TMPDIR=`mktemp -d /tmp/$PROJECT_NAME.XXXXXX` || exit 1
mkdir -p $TMPDIR
echo "=> Copying clone to $TMPDIR"
cp -LR * \
    .elasticbeanstalk \
    .git \
    .gitignore \
    $TMPDIR

cd $TMPDIR

PATH_EBEXTENSIONS=$(get_deploy_config_path)

copy_ebextension() {
    NAME=$1
    echo "=> Generating .ebextensions/$NAME"
    mkdir -p .ebextensions
    cp $PATH_EBEXTENSIONS/$NAME .ebextensions/$NAME
}

copy_ebextension autoscaling.config
copy_ebextension aws-secrets.config
copy_ebextension healthcheckurl.config
copy_ebextension notifications.config
copy_ebextension rolling-updates.config

# Adjust autoscaling group's size
echo "=> Adjusting autoscaling group's sizes ($AUTO_MINSIZE:$AUTO_MAXSIZE)"
cat .ebextensions/autoscaling.config \
    | sed -e "s|KLUE_MINSIZE|$AUTO_MINSIZE|g" \
    | sed -e "s|KLUE_MAXSIZE|$AUTO_MAXSIZE|g" \
    > .ebextensions/autoscaling.config.new
mv .ebextensions/autoscaling.config.new .ebextensions/autoscaling.config

echo "=> Generating .ebextensions/aws-secrets.config"
mkdir -p .ebextensions
echo "option_settings:" > .ebextensions/aws-secrets.config
for VAR in $(klue_config --env-secrets)
do
    echo "   Adding $VAR"
    VALUE=$(env | grep "^$VAR=" | cut -d '=' -f 2)
    if [ -z "$VALUE" ]; then
        echo "ERROR: variable $VAR has no value in env"
        exit 1
    fi
    echo "  - option_name: $VAR" >> .ebextensions/aws-secrets.config
    echo "    value: $VALUE" >> .ebextensions/aws-secrets.config
done


echo "=> Generating Dockerrun.aws.json for version $VERSION"
cat $DOCKERRUN_TEMPLATE \
    | sed -e "s|FROM whatever/|FROM $DOCKER_ROOT_REPO/|g" \
    | sed -e "s|whatever/foobar|$DOCKER_REPO|g" \
    | sed -e "s|VERSION|$VERSION|" \
    | sed -e "s|DOCKER_BUCKET|$DOCKER_BUCKET|" \
    > Dockerrun.aws.json

echo "=> Re-add .ebextensions to git"
cat .gitignore | grep -v ebextensions > .gitignore.tmp
mv .gitignore.tmp .gitignore

echo "=> Commiting aws-secrets and Dockerrun.aws.json (temporary)"
COMMIT_MSG="Commiting Dockerrun.aws.json and .ebextensions prior to eb deploy (Please undo after)"
git add -f Dockerrun.aws.json
git add -f .ebextensions/*
git add -f .gitignore
git commit -m "$COMMIT_MSG"

# TODO: make sure there is an environment. Create one?
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_DEFAULT_REGION

CURRENT_ENV_NAME=$(get_live_environment)
echo "=> Current live environment is $CURRENT_ENV_NAME"


# From here on, handle errors manually
set +e

#
# Doing a blue/green deployment - No downtime
#

NEW_ENV_NAME=$PROJECT_NAME-$VERSION

C=$(eb list | grep $NEW_ENV_NAME | wc -l)
if [ $C -ne 0 ]; then
    echo "ERROR: this environment is already deployed. Delete it first."
    exit 1
fi

if [ $DEPLOY_ONLY -eq 1 ]; then

    #
    # Doing an eb deploy - This will cause downtime
    #

    echo "=> Deploying $PROJECT_NAME $VERSION to new environment"
    eb deploy \
        --label $VERSION \
        --profile $EB_PROFILE \
        --timeout $EB_TIMEOUT \
        $NEW_ENV_NAME

    RC=$?
    if [ "$RC" -ne 0 ]; then
        # tell_slack "Failed to deploy version $VERSION of $PROJECT_NAME to environment $NEW_ENV_NAME - deploy aborted"
        exit 1
    fi

    cleanup
    exit 0
fi

# Create new environment
echo ""
echo "=> Creating new environment $NEW_ENV_NAME running sample app"
eb create \
    --profile $EB_PROFILE \
    --timeout $EB_TIMEOUT \
    --instance_type $EB_INSTANCE_TYPE \
    --platform $EB_PLATFORM \
    --keyname $AWS_KEYNAME \
    --sample \
    $NEW_ENV_NAME

RC=$?
if [ "$RC" -ne 0 ]; then
    # tell_slack "Failed to create environment $NEW_ENV_NAME - deploy aborted"
    exit 1
fi

# Deploy to new environment
echo ""
echo "=> Deploying $PROJECT_NAME $VERSION to new environment"
eb deploy \
    --label $VERSION \
    --profile $EB_PROFILE \
    --timeout $EB_TIMEOUT \
    $NEW_ENV_NAME

RC=$?
if [ "$RC" -ne 0 ]; then
    # tell_slack "Failed to deploy version $VERSION of $PROJECT_NAME to environment $NEW_ENV_NAME - deploy aborted"
    exit 1
fi

TEST_PORT=80

if [ ! -z "$ARN_CERT" ]; then

    TEST_PORT=443

    echo ""
    echo "=> Reconfiguring environment to use both HTTP and HTTPS"

    # These services listen on 443 only
    cat <<EOF > elb-https.json
[
    {
        "Namespace": "aws:elb:listener:443",
        "OptionName": "SSLCertificateId",
        "Value": "$ARN_CERT"
    },
    {
        "Namespace": "aws:elb:listener:443",
        "OptionName": "ListenerProtocol",
        "Value": "HTTPS"
    },
    {
        "Namespace": "aws:elb:listener:443",
        "OptionName": "InstancePort",
        "Value": "80"
    },
    {
        "Namespace": "aws:elb:listener:443",
        "OptionName": "InstanceProtocol",
        "Value": "HTTP"
    },
    {
        "Namespace": "aws:elb:listener:80",
        "OptionName": "ListenerEnabled",
        "Value": "true"
    },
    {
        "Namespace": "aws:elb:listener:80",
        "OptionName": "ListenerProtocol",
        "Value": "HTTP"
    },
    {
        "Namespace": "aws:elb:listener:80",
        "OptionName": "InstanceProtocol",
        "Value": "HTTP"
    },
    {
        "Namespace": "aws:elb:listener:443",
        "OptionName": "InstancePort",
        "Value": "80"
    }
]
EOF

    aws elasticbeanstalk update-environment \
        --environment-name $NEW_ENV_NAME \
        --profile $EB_PROFILE \
        --option-settings file://elb-https.json

    RC=$?
    if [ "$RC" -ne 0 ]; then
        tell_slack "Failed to set HTTPS on $NEW_ENV_NAME - deploy aborted"
        exit 1
    fi

    # Now wait for environment to get ready status
    echo "=> Waiting for environment to be Ready"
    sleep 3

    STATUS=0
    while [ "$STATUS" -ne 1 ];
    do
        echo "*"
        sleep 10
        STATUS=$(eb status $NEW_ENV_NAME --profile $EB_PROFILE | grep 'Ready' | wc -l)
    done
fi

# Run tests
URL=$(aws elasticbeanstalk describe-environments --profile $EB_PROFILE --environment-names $NEW_ENV_NAME | grep EndpointURL | awk '{print $2}' | sed -e 's/[\"\,]//g')
echo ""
echo "=> New environment is live at $URL"
cd $ROOTDIR
run_acceptance_tests --host $URL --port $TEST_PORT --no-ssl-check
RC=$?
if [ "$RC" -ne 0 ]; then
    tell_slack "Acceptance tests failed against $URL - deploy aborted"
    exit 1
fi

# Tests passed: swap cnames!
echo ""
echo "=> Swapping CNAMES between $NEW_ENV_NAME and $CURRENT_ENV_NAME"
eb swap $NEW_ENV_NAME -n $CURRENT_ENV_NAME
RC=$?
if [ "$RC" -ne 0 ]; then
    tell_slack "Failed to swap cnames between $NEW_ENV_NAME and $CURRENT_ENV_NAME - deploy aborted"
    exit 1
fi

tell_slack "Release of $PROJECT_NAME successful."

# Use new environment as default
echo "=> Using $NEW_ENV_NAME as default environment"
eb use $NEW_ENV_NAME

cleanup

