#!/usr/bin/env bash

# Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>

set -ue

BASEDIR="${BASEDIR:-"/usr/local/percona/qan-api"}"
ADDRESS="${ADDRESS:-"$(hostname -f)"}"
LISTEN="${LISTEN:-"0.0.0.0:9001"}"
IFIP="${LISTEN%:*}"
PORT="${LISTEN#*:}"
DEV="${DEV:-"no"}"
DB_PREFIX=""
SETUP_MYSQL="${SETUP_MYSQL:-"yes"}"
CREATE_DB="${CREATE_DB:-"yes"}"
CHECK_MYSQL="${CHECK_MYSQL:-"yes"}"
START="${START:-"yes"}"
SYSINT="${SYSINT:-"yes"}"

err() {
   echo "ERROR: $*" >&2
   exit 1
}

# Don't let use specify port via ADDRESS to avoid confusion with LISTEN=IP:PORT.
HOSTHOST="${ADDRESS%:*}"
HOSTPORT="${ADDRESS#*:}"
if [ "$HOSTHOST" != "$HOSTPORT" ]; then
   err "ADDRESS=$ADDRESS specifies a port: $HOSTPORT. Use LISTEN to specify a port. For example:" \
      "LISTEN=\"$IFIP:$HOSTPORT\" ADDRESS=\"$HOSTHOST\""
fi

MYSQL_USER="qan-api"
[ "$DEV" = "yes" ] && MYSQL_USER="percona"

DISTRO=""
[ -f "/etc/debian_version" ] && DISTRO="debian"
[ -f "/etc/redhat-release" ] && DISTRO="redhat"

BIN="percona-qan-api"
INIT_SCRIPT="/etc/init.d/$BIN"
KERNEL=`uname -s`

usage() {
   echo "Usage: [ENV_VAR=VAL,...] $0"
   echo
   echo "Environment variables:"
   echo "  ADDRESS      IP or FQDN of server            ($ADDRESS)"
   echo "  BASEDIR      Where to install                ($BASEDIR)"
   echo "  CHECK_MYSQL  Verify that MySQL is configured ($CHECK_MYSQL)"
   echo "  CREATE_DB    Create pmm schema               ($CREATE_DB)"
   echo "  LISTEN       IP:PORT to bind to              ($LISTEN)"
   echo "  SETUP_MYSQL  Configure MySQL as needed       ($SETUP_MYSQL)"
   echo "  START        Run API after install           ($START)"
   echo "  SYSINT       Install init.d script           ($SYSINT)"
   echo
   echo "See https://www.percona.com/doc/percona-monitoring-and-management/index.html for more info."
}

load_schema() {
   [ "$CREATE_DB" = "no" ] && return
   local db="${DB_PREFIX}pmm"
   echo "Creating $db database..."
   mysql -e "DROP DATABASE IF EXISTS $db"
   mysql -e "CREATE DATABASE $db"
   mysql "$db" < ./schema/pmm.sql
}

db_exists() {
   local exists="$(mysql -ss -e "SHOW DATABASES LIKE '$1'")"
   [ "$exists" ] && return 0 # exists
   return 1 # doesn't exist
}

app_is_running() {
   APP_PID="$(ps ax | grep "percona-qan-api" | grep "\-srcPath $BASEDIR" | grep -v grep | awk '{print $1}')"
   [ "$APP_PID" ] && return 0 # running
   return 1 # not running
}

run_app() {
   if [ "$KERNEL" != "Darwin" ]; then
      "$INIT_SCRIPT" start
   else
      "$BASEDIR/start"
   fi

   if [ $? -ne 0 ]; then
      err "$BIN did not start, check $BASEDIR/$BIN.log"
   else
      if [ "$IFIP" = "0.0.0.0" ]; then
         echo
         echo "WARNING: Percona Query Analytics API is listening on all interfaces ($LISTEN). This could be a security risk." \
            "To listen on a specific interface, reinstall and specify environment variable LISTEN=<interface IP>:<port>." \
            "For example: LISTEN=10.0.0.1:9001 ./install"
         echo
         sleep 1
      fi
      echo "Started Percona Query Analytics API on $LISTEN ($ADDRESS:$PORT). To check its log file:"
      echo
      echo "  tail -f $BASEDIR/$BIN.log"
   fi
}

check_req() {
   echo "Verify MySQL..."

   local myconn1="$(mysql -ss -e "SELECT CONCAT(@@hostname, ':', @@port)")"
   if [ -z "$myconn1" ]; then
      echo
      echo "Cannot execute mysql (see error above). Make sure MySQL is running" \
         "and that your MySQL user and pass in ~/.my.cnf are correct." >&2
      return 1
   else
      echo "OK: mysql connects to $myconn1 (if not correct, check your ~/.my.cnf)"
   fi
      
   local grants="$(mysql -e "SHOW GRANTS FOR '$MYSQL_USER'@'localhost'" 2>/dev/null)"
   if [ -z "$grants" ]; then
      # The MySQL user does not exist.
      local super="$(mysql -e "SHOW GRANTS" | grep 'ALL PRIV' | grep 'WITH GRANT OPTION')"
      if [ -z "$super" ]; then
         # We don't have the GRANT option with MySQL, so we can't create the MySQL user.
         echo
         echo "The '$MYSQL_USER' MySQL user does not exist, and it cannot be created because" \
            "the MySQL privs for this user do not include 'ALL PRIVILEGES ... WITH GRANT OPTION'." \
            "See the README.md learn how to create the MySQL user, or edit ~/.my.cnf" \
            "to connect to MySQL with root privs to create the MySQL user automatically." >&2
         return 1
      else
         # We'll create the MySQL user later.
         CREATE_PERCONA_MYSQL_USER="yes"
         echo "OK: MySQL user '$MYSQL_USER' will be created"
      fi
   else
      # The MySQL user already exists. Make sure we can use it.
      cp templates/percona.my.cnf .
      local mycnf="./percona.my.cnf"
      [ -f ~/.my.cnf ] && grep -e host -e port -e socket ~/.my.cnf >> "$mycnf"
      local myconn2="$(mysql --defaults-file=$mycnf -ss -e "SELECT CONCAT(@@hostname, ':', @@port)")"
      if [ -z "$myconn2" ]; then
         echo >&2
         echo "Cannot connect to MySQL using the '$MYSQL_USER' user. See the README.md" \
            "to learn what password and privs it should have. If the privs are correct," \
            "this check might have a bug: verify that $mycnf is correct; it was created from" \
            " templates/percona.my.cnf and ~/.my.cnf." >&2
         return 1
      else
         rm -f "$mycnf"
         echo "OK: MySQL user '$MYSQL_USER' exists"
      fi
   fi
}

# ###########################################################################
# Execution starts here
# ###########################################################################

# Get and cd to dir where this script and the rest of the pkg files are.
PKG_DIR="$(cd "$(dirname "$0")"; pwd)"
cd "$PKG_DIR"

CREATE_PERCONA_MYSQL_USER="no"

if [ $# -eq 0 ]; then 
   # Check system requirements, best effort to ensure install will succeed.
   if [ "$SETUP_MYSQL" = "yes" -a "$CHECK_MYSQL" = "yes" ]; then
      check_req || exit 1
   fi

   # Check if the schemas exist already. Only drop and create when forced.
   if [ "$SETUP_MYSQL" = "yes" -a "$CREATE_DB" = "yes" ]; then
      if db_exists "${DB_PREFIX}pmm"; then
         if [ "$CREATE_DB" != "force" ]; then
            err "Database ${DB_PREFIX}pmm exists. Reinstall and" \
               "specify environment variable CREATE_DB=no to use it, or" \
               "CREATE_DB=force to drop and recreate it."
         fi
      fi
   fi

   # Stop API if it's running, else cp will throw error:
   # cp: cannot create regular file `/usr/local/percona/qan-api/percona-qan-api': Text file busy
   if app_is_running; then
      kill $APP_PID
      [ -f "$BASEDIR/$BIN.pid" ] && rm -f "$BASEDIR/$BIN.pid"
   fi

   if [ ! -d "$BASEDIR" ]; then
      mkdir -p "$BASEDIR" || err "Cannot create $BASEDIR"
   fi

   # Test privs on the basedir, else cp -r will spew errors.
   touch "$BASEDIR/fipar" || err "Cannot write to $BASEDIR"
   rm "$BASEDIR/fipar"    || err "fipar cannot be removed"

   # Copy everything in the pkg to the basedir, but remove this script and the init script.
   cp -r * "$BASEDIR"
   rm "$BASEDIR/install"
   rm "$BASEDIR/percona-qan-api"

   if [ "$DEV" = "yes" ]; then
      pass="percona"
   else
      pass="$(awk '/password/ { print $3 }' templates/percona.my.cnf)"
   fi 

   APP_CONF="src/github.com/percona/qan-api/conf/app.conf"
   sed -e "s/= 9001/= $PORT/g" -e "s/http.addr.*/http.addr = $IFIP/" "./$APP_CONF" > "$BASEDIR/$APP_CONF"

   PROD_CONF="src/github.com/percona/qan-api/conf/prod.conf"
   sed -e "s/:host/$ADDRESS/g" -e "s/:port/$PORT/g" -e "s/percona:percona/$MYSQL_USER:$pass/g" "./$PROD_CONF" > "$BASEDIR/$PROD_CONF"

   cd "$BASEDIR"

   if [ "$SETUP_MYSQL" = "yes" ]; then
      load_schema

      # Create the percona MySQL user if check_req detected that it doesn't exist
      # and we have a root MySQL connection to create it.
      if [ "$CREATE_PERCONA_MYSQL_USER" = "yes" ]; then
         mysql -e "GRANT ALL ON ${DB_PREFIX}pmm.* TO '$MYSQL_USER'@'localhost' IDENTIFIED BY '$pass'"
         echo "Created MySQL user '$MYSQL_USER'@'localhost'"
      fi
   else
      echo "Not configuring MySQL because SETUP_MYSQL != \"yes\"." \
         "Ensure MySQL is property configured before starting the QAN API."
   fi

   # Remove the templates dir because we don't need it post-install, and it
   # contains the percona MySQL user password.
   rm -rf templates/

   # Register the init script to make "service percona-qan-api start|stop" work.
   if [ "$SYSINT" = "yes" ]; then
      if [ "$KERNEL" != "Darwin" ]; then
         cp -f "$PKG_DIR/$BIN" "$INIT_SCRIPT"
         chmod a+x "$INIT_SCRIPT"
         if hash update-rc.d 2>/dev/null; then
            echo "Using update-rc.d to install $BIN service"
            update-rc.d $BIN defaults >/dev/null
         elif hash chkconfig 2>/dev/null; then
            echo "Using chkconfig to install $BIN service"
            chkconfig $BIN on >/dev/null
         else
            echo "Cannot find chkconfig or update-rc.d. $BIN is installed," \
               "but it will not start automatically when the server start." \
               "To start it manually, run: $BASEDIR/start" >&2
         fi
      else
         echo "Mac OS detected, not installing sys-init script"
      fi
   fi

   if [ "$START" = "yes" ]; then
      run_app 
      echo
      echo "To test the connection to the API, this command should return \"HTTP/1.1 200 OK\" and other HTTP headers:"
      echo
      echo "  curl -I http://$ADDRESS:$PORT/ping"
      echo
      if [ "$IFIP" != "0.0.0.0" ]; then
         echo "Be sure that $ADDRESS resolves to $IFIP, else connections to the API will fail."
         echo
      fi
   else
      echo
      echo "To start the API:"
      echo
      if [ "$KERNEL" = "Darwin" ]; then
         echo "  $BASEDIR/start"
      else
         echo "  service percona-qan-api start"
      fi
      echo
   fi
elif [ $# -eq 1 ]; then
   case $1 in
      help)
         usage
         exit 0
         ;;
      check)
         if check_req; then
            exit 0
         else
            exit 1
         fi
         ;;
      *)
         echo "Unknown command: '$1'" >&2
         echo "Run without arguments to list commands." >&2
         exit 1
         ;;
   esac
else
   usage
   exit 1
fi
