#!/bin/bash

export PATH="/var/vcap/packages/cf_cli/bin:$PATH"
export CF=`which cf`

function cf() {
	echo cf "$@"
	output=$($CF "$@" 2>&1)
	result="$?"
	if [ "$result" -ne "0" ]; then
		echo "$output"
		exit $result
	fi
}

function is_true() {
	equals_ignore_case "$1" "true"
}

function equals_ignore_case() {
	echo "$1" | grep -i "^$2\$" >/dev/null
}

function import_opsmgr_variables() {
	export SCHEME=https
	export ADMIN_USER={{ 'properties.cf.admin_user' | shell_string }}
	export ADMIN_PASSWORD={{ 'properties.cf.admin_password' | shell_string }}
	export DOMAIN={{ 'properties.domain' | shell_string }}
	export APP_DOMAIN={{ 'properties.app_domains[0]' | shell_string }}
	export CF_ORG={{ 'properties.org' | shell_string }}
	export CF_SPACE={{ 'properties.space' | shell_string }}
	export CF_TARGET=$SCHEME://api.${DOMAIN}
	export CF_SKIP_SSL={{ 'properties.ssl.skip_cert_verify' | shell_string }}
	export SECURITY_USER_NAME={{ 'properties.security.user' | shell_string }}
	export SECURITY_USER_PASSWORD={{ 'properties.security.password' | shell_string }}
	export APPLY_OPEN_SECURITY_GROUP={{ 'properties.apply_open_security_group' | shell_string }}
	export ALLOW_PAID_SERVICE_PLANS={{ 'properties.allow_paid_service_plans' | shell_string }}

	{% for service_plan_form in context.service_plan_forms %}
	{{ service_plan_form | plans_json | indent }}
	{% endfor %}

	{% for property in context.all_properties %}
	{{ property | env_variable | indent }}
	{% endfor %}
	{% for package in context.packages if package.type == 'docker-bosh' %}
	export {{ package.name | upper }}_HOST={{ ( 'properties.' + package.name + '.host' ) | shell_string }}
	export {{ package.name | upper }}_HOSTS={{ ( 'properties.' + package.name + '.hosts' ) | shell_string }}
	{% endfor %}
	{% for package in context.packages if package.type == 'bosh-release' %}
	{% for job in package.jobs %}
	{% if job.is_static %}
	export {{ package.name | upper }}_{{ job.varname | upper }}_HOST={{ ( 'properties.' + package.name + '.' + job.varname + '.host' ) | shell_string }}
	export {{ package.name | upper }}_{{ job.varname | upper }}_HOSTS={{ ( 'properties.' + package.name + '.' + job.varname + '.hosts' ) | shell_string }}
	{% endif %}
	{% endfor %}
	{% endfor %}
}

function prepare_cf_cli {
	export PATH="/var/vcap/packages/cf_cli/bin:$PATH"
	export CF_HOME=`pwd`/home/cf
	mkdir -p $CF_HOME
}

function authenticate() {
	$CF --version
	if is_true "$CF_SKIP_SSL"; then
		cf api $CF_TARGET --skip-ssl-validation
	else
		cf api $CF_TARGET
	fi
	cf auth $ADMIN_USER $ADMIN_PASSWORD
}

function setup_target_org() {
	if [ -z "$CF_ORG" ]; then
		CF_ORG={{ context.name }}-org
	fi
	if ! $CF org $CF_ORG >/dev/null; then
		cf create-org $CF_ORG
		ignore_failure=`$CF set-quota $CF_ORG runaway`
	fi
	cf target -o $CF_ORG
}

function setup_target_space() {
	if [ -z "$CF_SPACE" ]; then
		CF_SPACE={{ context.name }}-space
	fi
	if ! $CF space $CF_SPACE >/dev/null; then
		cf create-space $CF_SPACE
	fi
	cf target -s $CF_SPACE
}

function add_target_org_to_admin() {

    SYSTEM_ORG_GUID=`$CF org system --guid`
    NEW_ORG_GUID=`$CF org $CF_ORG --guid`

    ADMIN_GUID=`$CF curl /v2/organizations/$SYSTEM_ORG_GUID/users | grep -A 2 "username\": \"admin" | awk '{print $2}' | grep organizations | awk -F '/' '{print $4 }' `

	$CF curl /v2/users/$ADMIN_GUID/organizations/$NEW_ORG_GUID -X PUT >/dev/null
	$CF curl /v2/users/$ADMIN_GUID/managed_organizations/$NEW_ORG_GUID -X PUT >/dev/null
}

function setup_package_path() {
	if [ -z "$PACKAGE_PATH" ]; then
		export PACKAGE_PATH=/var/vcap/packages
	fi
}

function apply_open_security_group() {
	if ! is_true "$1"; then
		return
	fi
	if ! $CF security-group all_open >/dev/null; then
		cf create-security-group all_open ${PACKAGE_PATH}/cf_cli/all_open.json
	fi
	cf bind-running-security-group all_open
}

function add_env_vars() {
	cf set-env $1 UAA_HOST "$SCHEME://uaa.$DOMAIN"
	cf set-env $1 CC_HOST "$CF_TARGET"
	cf set-env $1 LOGIN_HOST "$SCHEME://login.$DOMAIN"
	cf set-env $1 ROOT "\$HOME"
	cf set-env $1 SCHEME "$SCHEME"
	cf set-env $1 VERIFY_SSL "$CF_SKIP_SSL"

	# Adding additional properties that most spring based apps or internal apps seem to be expecting
	cf set-env $1 CF_ORG "$CF_ORG"
	cf set-env $1 CF_SPACE "$CF_SPACE"
	cf set-env $1 CF_TARGET "$CF_TARGET"
	cf set-env $1 CF_SKIP_SSL "$CF_SKIP_SSL"
	cf set-env $1 SECURITY_USER_NAME "$SECURITY_USER_NAME"
	cf set-env $1 SECURITY_USER_PASSWORD "$SECURITY_USER_PASSWORD"

	# Custom Service Plans
	{% for service_plan_form in context.service_plan_forms %}
	cf set-env $1 {{ service_plan_form.variable_name }} "${{ service_plan_form.variable_name }}"
	{% endfor %}

	# Custom variables from tile.yml
	{% for property in context.all_properties %}
	cf set-env $1 {{ property.name | upper }} "${{ property.name | upper }}"
	{% endfor %}

	# Addresses of bosh vms
	{% for package in context.packages if package.type == 'docker-bosh' %}
	cf set-env $1 {{ package.name | upper }}_HOST "${{ package.name | upper }}_HOST"
	cf set-env $1 {{ package.name | upper }}_HOSTS "${{ package.name | upper }}_HOSTS"
	{% endfor %}
	{% for package in context.packages if package.type == 'bosh-release' %}
	{% for job in package.jobs %}
	{% if job.is_static %}
	cf set-env $1 {{ package.name | upper }}_{{ job.varname | upper }}_HOST "${{ package.name | upper }}_{{ job.varname | upper }}_HOST"
	cf set-env $1 {{ package.name | upper }}_{{ job.varname | upper }}_HOSTS "${{ package.name | upper }}_{{ job.varname | upper }}_HOSTS"
	{% endif %}
	{% endfor %}
	{% endfor %}
}

function add_cf_credentials() {
	cf set-env $1 CF_ADMIN_USER "$ADMIN_USER"
	cf set-env $1 CF_ADMIN_USERNAME "$ADMIN_USER"
	cf set-env $1 CF_ADMIN_PASSWORD "$ADMIN_PASSWORD"
}

function delete_older_versions() {
	PKG_NAME="$1"
	APP_NAME="$2"
	$CF apps | grep "^$PKG_NAME" | grep -v "^$APP_NAME" | while read app rest; do
		cf delete -f $app
	done
}

function wait_till_running() {
	for i in `seq 1  6`; do
		CF_TRACE=true $CF app "$1" | grep RUNNING | grep "$1" >/dev/null
		if [ "$?" -ne "0" ]; then
			sleep 10
			echo "cf app $1   # waiting for running state"
		else
			break
		fi
	done
}

function deploy_app() {
	PKG_NAME="$1"
	APP_NAME="$2"
	APP_HOST="$3"
	DOCKER_IMAGE="$4"
	NEEDS_CF_CREDS="$5"
	HEALTH_CHECK="$6"
	cf push ${APP_NAME} -n ${APP_HOST} -d ${APP_DOMAIN} \
		${DOCKER_IMAGE} -f $PACKAGE_PATH/${PKG_NAME}/manifest.yml \
		--no-start
	add_env_vars ${APP_NAME}
	if is_true "$NEEDS_CF_CREDS"; then
		add_cf_credentials ${APP_NAME}
	fi
	if [ -n "$HEALTH_CHECK" ]; then
		cf set-health-check ${APP_NAME} "$HEALTH_CHECK"
	fi
}

function start_app() {
	PKG_NAME="$1"
	APP_NAME="$2"
	echo cf start ${APP_NAME}
	$CF start ${APP_NAME}
	result="$?"
	if [ "$result" -ne "0" ]; then
		echo cf logs ${APP_NAME} --recent
		$CF logs ${APP_NAME} --recent
		exit $result
	fi
	wait_till_running ${APP_NAME}
	delete_older_versions ${PKG_NAME} ${APP_NAME}
}

function provision_auto_service() {
	PKG_NAME="$1"
	APP_NAME="$2"
	SVC_NAME="$3"
	SVC_PLAN="$4"
	if [ -z "${SVC_NAME}" ]; then
		return
	fi
	if equals_ignore_case "$SVC_NAME" "none"; then
		return
	fi
	$CF services | grep ${PKG_NAME}-${SVC_NAME} >/dev/null
	if [ "$?" -ne "0" ]; then
		DEFAULT_PLAN=`$CF marketplace | grep "${SVC_NAME}" | awk '{ print $2 }' | sed 's/,//g'`
		if [ -n "$DEFAULT_PLAN" ]; then
			if [ -z "$SVC_PLAN" ]; then
				SVC_PLAN="${DEFAULT_PLAN}"
			fi
			cf create-service ${SVC_NAME} ${SVC_PLAN} ${PKG_NAME}-${SVC_NAME}
		else
			echo "no service matching" ${SVC_NAME} "in marketplace"
			exit 1
		fi
	elif [ -n "$SVC_PLAN" ]; then
		cf update-service ${PKG_NAME}-${SVC_NAME} -p ${SVC_PLAN}
	fi
	cf bind-service ${APP_NAME} ${PKG_NAME}-${SVC_NAME}
}

function register_broker() {
	BROKER_NAME="$1"
	BROKER_USER="$2"
	BROKER_PASS="$3"
	BROKER_URL="$4"
	GLOBAL_ACCESS="$5"
	BROKER_SERVICES="$6"
	$CF service-brokers | grep $BROKER_NAME >/dev/null
	if [ "$?" -ne "0" ]; then
		cf create-service-broker "$BROKER_NAME" "$BROKER_USER" "$BROKER_PASS" "$BROKER_URL"
	else
		cf update-service-broker "$BROKER_NAME" "$BROKER_USER" "$BROKER_PASS" "$BROKER_URL"
	fi
	if is_true "$GLOBAL_ACCESS"; then
		publicize_services "$BROKER_NAME" "$BROKER_SERVICES"
	fi
}

function publicize_services() {
	BROKER_NAME="$1"
	BROKER_SERVICES="$2"
	services_url=`$CF curl /v2/services?q=label:$BROKER_NAME | grep service_plans_url | awk '{ print $2 }' | sed 's/[",]//g'`
	if [ -n "$services_url" ]; then
		services=`$CF curl $services_url | grep "\"url\"" | awk '{ print $2 }' | sed 's/[",]//g'`
		for service in $services; do
			cf curl $service -X PUT -d '{"public": true}'
		done
	elif [ -n "$BROKER_SERVICES" ] && [ "$BROKER_SERVICES" != "INTERNAL_SERVICE_NAME" ]; then
		# Rely on the internal plan names if we cannot get it from the /v2/services on the broker
		for service_name in `echo "$BROKER_SERVICES" | sed -e 's/,/ /g;s/;/ /g'`
		do
			cf enable-service-access $service_name
		done
	else
		services=`$CF service-access | awk "/broker: ${BROKER_NAME}/{flag=1;next}/broker: /{flag=0}flag" | egrep -v "access *orgs" | grep "."  | awk '{print $1}' | uniq `
		for service_name in $services; do
			cf enable-service-access $service_name
		done
	fi
}

function push_buildpack() {
	BUILDPACK_NAME="$1"
	BUILDPACK_ORDER="$2"
	BUILDPACK_FILE="$3"
	BUILDPACK_VERSIONED=`echo "${BUILDPACK_FILE}" | sed 's/\.zip$/-v{{ context.version }}.zip/'`
	cp "${BUILDPACK_FILE}" "${BUILDPACK_VERSIONED}"
	$CF buildpacks | grep ${BUILDPACK_NAME} >/dev/null
	if [ "$?" -eq "0" ]; then
		cf update-buildpack $BUILDPACK_NAME -p $BUILDPACK_VERSIONED --enable
	else
		cf create-buildpack $BUILDPACK_NAME $BUILDPACK_VERSIONED $BUILDPACK_ORDER --enable
	fi
}

{% raw %}
# Function for version comparison based on a code fragment from Stack Overflow
# http://stackoverflow.com/questions/4023830/how-compare-two-strings-in-dot-separated-version-format-in-bash
# By Dennis Williamson (http://stackoverflow.com/users/26428/dennis-williamson)
#
function is_newer() {
	arg1=`echo "$1" | sed 's/\([0-9]+(\.[0-9]+)*\)/\1/'`
	arg2=`echo "$2" | sed 's/\([0-9]+(\.[0-9]+)*\)/\1/'`
    if [[ "$arg1" == "$arg2" ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=($arg1) ver2=($arg2)
    # fill empty fields in ver1 with zeros
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
    do
        ver1[i]=0
    done
    for ((i=0; i<${#ver1[@]}; i++))
    do
        if [[ -z ${ver2[i]} ]]
        then
            # fill empty fields in ver2 with zeros
            ver2[i]=0
        fi
        if ((10#${ver1[i]} > 10#${ver2[i]}))
        then
            return 0
        fi
        if ((10#${ver1[i]} < 10#${ver2[i]}))
        then
            return 1
        fi
    done
    return 0
}
{% endraw %}

import_opsmgr_variables
prepare_cf_cli
authenticate
setup_target_org
setup_target_space
add_target_org_to_admin
setup_package_path
apply_open_security_group "$APPLY_OPEN_SECURITY_GROUP"

{% for package in context.packages %}
# Deploy package {{ package.name }}
#

{% if package.pre_deploy %}
{{ package.pre_deploy | render }}
{% endif %}

{% if package.deploy %}
{{ package.deploy | render }}
{% else %}

{% if package.is_app %}
export PKG_NAME={{ package.name }}
export APP_NAME={{ package.name | hyphens }}-{{ context.version }}
export APP_HOST={{ (package.manifest and package.manifest.host) or package.name | hyphens }}
export DOCKER_IMAGE="{{ ( package.is_docker and '--docker-image ' + package.image ) or '' }}"
export NEEDS_CF_CREDS="{{ package.needs_cf_credentials }}"
export HEALTH_CHECK="{{ package.health_check }}"
deploy_app "$PKG_NAME" "$APP_NAME" "$APP_HOST" "$DOCKER_IMAGE" "$NEEDS_CF_CREDS" "$HEALTH_CHECK"
{% for service in package.auto_services %}
export SVC_NAME="{{ service.name }}"
export SVC_PLAN="{{ service.plan or '' }}"
provision_auto_service "$PKG_NAME" "$APP_NAME" "$SVC_NAME" "$SVC_PLAN"
{% endfor %}
start_app {{ package.name | hyphens }} "$APP_NAME"
{% endif %}

{% if package.is_broker %}
export BROKER_NAME={{ package.name | hyphens }}
export BROKER_USER={{ ( 'properties.' + package.name + '.user' ) | shell_string }}
export BROKER_PASS={{ ( 'properties.' + package.name + '.password' ) | shell_string }}
export BROKER_SERVICES="{{ package.internal_service_names }}"
{% if package.is_external_broker %}
export BROKER_URL={{ ( 'properties.' + package.name + '.url' ) | shell_string }}
{% else %}
export BROKER_URL=$SCHEME://${APP_HOST}.${APP_DOMAIN}
{% endif %}
export GLOBAL_ACCESS={{ ( 'properties.' + package.name + '.enable_global_access_to_plans' ) | shell_string }}
register_broker "$BROKER_NAME" "$BROKER_USER" "$BROKER_PASS" "$BROKER_URL" "$GLOBAL_ACCESS" "$BROKER_SERVICES"
{% endif %}

{% if package.is_buildpack %}
export BUILDPACK_NAME={{ package.name }}
export BUILDPACK_ORDER={{ ( 'properties.' + package.name + '.buildpack_order' ) | shell_string }}
export BUILDPACK_FILE=$PACKAGE_PATH/{{ package.name }}/{{ package.path }}
push_buildpack "$BUILDPACK_NAME" "$BUILDPACK_ORDER" "$BUILDPACK_FILE"
{% endif %}

{% endif %}

{% if package.post_deploy %}
{{ package.post_deploy | render }}
{% endif %}

{% endfor %}
