#!/usr/bin/env bash
set -eo pipefail
shopt -s nullglob

export DOKKU_DISTRO=${DOKKU_DISTRO:="ubuntu"}
export DOKKU_IMAGE=${DOKKU_IMAGE:="progrium/buildstep"}
export DOKKU_ROOT=${DOKKU_ROOT:=~dokku}

export PLUGIN_PATH=${PLUGIN_PATH:="/var/lib/dokku/plugins"}
export DOKKU_NOT_IMPLEMENTED_EXIT=10
export DOKKU_VALID_EXIT=0

[[ -f $DOKKU_ROOT/dokkurc ]] && source $DOKKU_ROOT/dokkurc
[[ -d $DOKKU_ROOT/.dokkurc ]] && for f in $DOKKU_ROOT/.dokkurc/*; do source $f; done

[[ $DOKKU_TRACE ]] && set -x

if [[ $(id -un) != "dokku" && $1 != plugins-install* && $1 != "plugins-update" ]]; then
  sudo -u dokku -E -H $0 "$@"
  exit
fi

if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
  export -n SSH_ORIGINAL_COMMAND
  if [[ $1 =~ config-* ]];then
    xargs $0 <<<$SSH_ORIGINAL_COMMAND
    exit $?
  else
    $0 $SSH_ORIGINAL_COMMAND
    exit $?
  fi
fi

case "$1" in
  receive)
    APP="$2"; IMAGE="dokku/$APP"
    echo "-----> Cleaning up ..."
    dokku cleanup
    echo "-----> Building $APP ..."
    cat | dokku build $APP
    echo "-----> Releasing $APP ..."
    dokku release $APP
    echo "-----> Deploying $APP ..."
    dokku deploy $APP
    echo "=====> Application deployed:"
         dokku urls $APP | sed "s/^/       /"
    echo
    ;;

  build)
    APP="$2"; IMAGE="dokku/$APP"; CACHE_DIR="$DOKKU_ROOT/$APP/cache"
    id=$(cat | docker run -i -a stdin $DOKKU_IMAGE /bin/bash -c "mkdir -p /app && tar -xC /app")
    test "$(docker wait $id)" -eq 0
    docker commit $id $IMAGE > /dev/null
    [[ -d $CACHE_DIR ]] || mkdir $CACHE_DIR
    pluginhook pre-build $APP
    id=$(docker run -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $IMAGE /build/builder)
    docker attach $id
    test "$(docker wait $id)" -eq 0
    docker commit $id $IMAGE > /dev/null
    pluginhook post-build $APP
    ;;

  release)
    APP="$2"; IMAGE="dokku/$APP"
    pluginhook pre-release $APP
    if [[ -f "$DOKKU_ROOT/$APP/ENV" ]]; then
      id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/app-env.sh" < "$DOKKU_ROOT/$APP/ENV")
      test "$(docker wait $id)" -eq 0
      docker commit $id $IMAGE > /dev/null
    fi
    pluginhook post-release $APP
    ;;

  deploy)
    APP="$2"; IMAGE="dokku/$APP"
    pluginhook pre-deploy $APP

    if [[ -f "$DOKKU_ROOT/$APP/CONTAINER" ]]; then
      oldid=$(< "$DOKKU_ROOT/$APP/CONTAINER")
    fi

    # start the app
    DOCKER_ARGS=$(: | pluginhook docker-args $APP deploy)
    BIND_EXTERNAL=$(pluginhook bind-external-ip $APP)

    if [[ "$BIND_EXTERNAL" = "false" ]];then
      port=5000
      id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE /bin/bash -c "/start web")
      ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id)
    else
      id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE /bin/bash -c "/start web")
      port=$(docker port $id 5000 | sed 's/[0-9.]*://')
      ipaddr=127.0.0.1
    fi
    echo $ipaddr > "$DOKKU_ROOT/$APP/IP"

    # if we can't post-deploy successfully, kill new container
    function kill_new {
      docker inspect $id &> /dev/null && docker kill $id > /dev/null
      trap - INT TERM EXIT
      kill -9 $$
    }

    # run checks first, then post-deploy hooks, which switches Nginx traffic
    trap kill_new INT TERM EXIT
    echo "-----> Running pre-flight checks"
    pluginhook check-deploy $id $APP $port ${ipaddr:-localhost}

    # now using the new container
    echo $id > "$DOKKU_ROOT/$APP/CONTAINER"
    echo $port > "$DOKKU_ROOT/$APP/PORT"
    echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"

    echo "-----> Running post-deploy"
    pluginhook post-deploy  $APP $port $ipaddr
    trap -        INT TERM EXIT

    # kill the old container
    if [[ -n "$oldid" ]]; then
      # Let the old container finish processing requests, before terminating it
      WAIT="${DOKKU_WAIT_TO_RETIRE:-60}"
      echo "-----> Shutting down old container in $WAIT seconds"
      (
        exec >/dev/null 2>/dev/null </dev/null
        trap '' INT HUP
        sleep $WAIT
        docker kill $oldid
      ) & disown -a
      # Use trap since disown/nohup don't seem to keep child alive
      # Give child process just enough time to set the traps
      sleep 0.1
    fi

    ;;

  cleanup)
    # delete all non-running container
    docker ps -a | grep 'Exit' | awk '{print $1}' | xargs docker rm &> /dev/null &
    # delete unused images
    docker images | grep '<none>' | awk '{print $3}' | xargs docker rmi &> /dev/null &
    ;;

  plugins)
    ls -1 -d $PLUGIN_PATH/*/
    ;;

  plugins-install)
    pluginhook install
    ;;

  plugins-install-dependencies)
    pluginhook dependencies
    ;;

  plugins-update)
    pluginhook update
    ;;

  # temporary hack for https://github.com/progrium/dokku/issues/82
  deploy:all)
    for app in $DOKKU_ROOT/*/CONTAINER; do
      APP=$(basename "$(dirname $app)");
      dokku deploy $APP
    done
    ;;

  help|'')
    echo "Usage: dokku COMMAND <app> [command-specific-options]"
    echo ""

    cat<<EOF | pluginhook commands help | sort
    help                                            Print the list of commands
    plugins                                         Print active plugins
    plugins-install                                 Install active plugins
    plugins-update                                  Update active plugins
EOF
    ;;

  *)
    implemented=0
    for script in $PLUGIN_PATH/*/commands; do
      set +e; $script "$@" ; exit_code=$? ; set -e
      if [ "$exit_code" -eq "$DOKKU_NOT_IMPLEMENTED_EXIT" ]; then
        continue
      fi

      implemented=1
      if [ "$exit_code" -ne "$DOKKU_VALID_EXIT" ]; then
        exit $exit_code
      fi
    done

    if [ "$implemented" -eq 0 ]; then
      echo " !    \`$*\` is not a dokku command."
      echo " !    See \`dokku help\` for a list of available commands."
      exit 1
    fi
    ;;

esac
