#!/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

source "$PLUGIN_PATH/common/functions"

[[ -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

parse_args "$@"
args=("$@")
if [[ "${args[0]}" =~ ^--.* ]]; then
  for arg in "$@"; do
    if [[ "$arg" =~ ^--.* ]];then
      shift 1
    else
      break
    fi
  done
fi
! has_tty && DOKKU_QUIET_OUTPUT=1

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-* ]] || [[ $1 =~ docker-options* ]];then
    xargs $0 <<<$SSH_ORIGINAL_COMMAND
    exit $?
  else
    $0 $SSH_ORIGINAL_COMMAND
    exit $?
  fi
fi

case "$1" in
  receive)
    APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4"
    dokku_log_info1 "Cleaning up..."
    dokku cleanup
    dokku_log_info1 "Building $APP from $IMAGE_SOURCE_TYPE..."
    dokku build "$APP" "$IMAGE_SOURCE_TYPE" "$TMP_WORK_DIR"
    dokku_log_info1 "Releasing $APP..."
    dokku release "$APP" "$IMAGE_SOURCE_TYPE"
    dokku_log_info1 "Deploying $APP..."
    dokku deploy "$APP"
    dokku_log_info2 "Application deployed:"
         dokku urls "$APP" | sed "s/^/       /"
    echo
    ;;

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

    is_image_buildstep_based "$IMAGE" && DOKKU_BUILDSTEP=true
    DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE"
    oldids=$(get_container_ids $APP)

    while read line || [ -n "$line" ]
    do
      PROC_TYPE=${line%%=*}
      PROC_COUNT=${line#*=}
      CONTAINER_INDEX=1

      while [[ $CONTAINER_INDEX -le $PROC_COUNT ]];do
        id=""; port=""; ipaddr=""
        DOKKU_CONTAINER_ID_FILE="$DOKKU_ROOT/$APP/CONTAINER.$PROC_TYPE.$CONTAINER_INDEX"
        DOKKU_IP_FILE="$DOKKU_ROOT/$APP/IP.$PROC_TYPE.$CONTAINER_INDEX"
        DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX"

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

        [[ -n "$DOKKU_BUILDSTEP" ]] && START_CMD="/start $PROC_TYPE"
        [[ -z "$DOKKU_BUILDSTEP" ]] && DOKKU_DOCKERFILE_PORT=$(dokku config:get $APP DOKKU_DOCKERFILE_PORT || true)

        if [[ "$BIND_EXTERNAL" = "false" ]] && [[ "$PROC_TYPE" == "web" ]];then
          port=${DOKKU_DOCKERFILE_PORT:=5000}
          id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD)
          ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id)
        elif [[ "$BIND_EXTERNAL" = "true" ]] && [[ "$PROC_TYPE" == "web" ]];then
          id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE $START_CMD)
          port=$(docker port $id 5000 | sed 's/[0-9.]*://')
          ipaddr=127.0.0.1
        elif [[ "$PROC_TYPE" != "web" ]];then
          id=$(docker run -d $DOCKER_ARGS $IMAGE $START_CMD)
        fi

        # if we can't post-deploy successfully, kill new container
        kill_new() {
          docker inspect $id &> /dev/null && docker stop $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
        dokku_log_info1 "Running pre-flight checks"
        pluginhook check-deploy  $APP $id $PROC_TYPE $port $ipaddr
        trap -        INT TERM EXIT

        # now using the new container
        [[ -n "$id" ]] && echo $id > "$DOKKU_CONTAINER_ID_FILE"
        [[ -n "$ipaddr" ]] && echo $ipaddr > "$DOKKU_IP_FILE"
        [[ -n "$port" ]] && echo $port > "$DOKKU_PORT_FILE"
        [[ -n "$port" ]] && echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"

        # cleanup pre-migration files
        rm -f $DOKKU_ROOT/$APP/CONTAINER $DOKKU_ROOT/$APP/IP $DOKKU_ROOT/$APP/PORT

        CONTAINER_INDEX=$(( CONTAINER_INDEX + 1 ))
      done
      # cleanup when we scale down
      if [[ "$PROC_COUNT" == 0 ]]; then
        CONTAINER_IDX_OFFSET=0
      else
        CONTAINER_IDX_OFFSET=$((PROC_COUNT + 1))
      fi
      find $DOKKU_ROOT/$APP -maxdepth 1 -name "CONTAINER.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
      find $DOKKU_ROOT/$APP -maxdepth 1 -name "IP.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
      find $DOKKU_ROOT/$APP -maxdepth 1 -name "PORT.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
    done < "$DOKKU_SCALE_FILE"

    dokku_log_info1 "Running post-deploy"
    pluginhook post-deploy $APP $port $ipaddr

    # kill the old container
    if [[ -n "$oldids" ]]; then
      # Let the old container finish processing requests, before terminating it
      WAIT="${DOKKU_WAIT_TO_RETIRE:-60}"
      dokku_log_info1 "Shutting down old containers in $WAIT seconds"
      for oldid in $oldids; do
        dokku_log_info2 "$oldid"
      done
      (
        exec >/dev/null 2>/dev/null </dev/null
        trap '' INT HUP
        sleep $WAIT
        for oldid in $oldids; do
          docker stop $oldid &> /dev/null
          docker kill $oldid &> /dev/null  # force a kill as docker seems to not send SIGKILL as the docs would indicate
        done
      ) & 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
    # shellcheck disable=SC2046
    docker rm $(docker ps -a -f 'status=exited' -q) &> /dev/null &
    # delete unused images
    # shellcheck disable=SC2046
    docker rmi $(docker images -f 'dangling=true' -q) &> /dev/null &
    ;;

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

  plugins-install)
    pluginhook install
    ;;

  plugins-install-dependencies)
    pluginhook dependencies
    ;;

  plugins-update)
    pluginhook update
    ;;

  # DEPRECATED as of v0.3.14
  deploy:all)
      echo "*DEPRECATED* in v0.3.14: deploy:all will be removed in future versions"
      dokku ps:restartall
    ;;

  help|'')
    echo "Usage: dokku [--quiet|--trace|--rm-container|--rm|--force] COMMAND <app> [command-specific-options]"
    echo ""
    echo "Options:"

    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
      dokku_log_warn "\`$*\` is not a dokku command."
      dokku_log_warn "See \`dokku help\` for a list of available commands."
      exit 1
    fi
    ;;

esac
