#!/bin/bash
SYS_PATH=/etc/pkgship
OUT_PATH=/opt/pkgship/uwsgi
OPERATION=$1
PKGSHIP_CONSTANT="pkgship"
MEM_THRESHOLD='2048'
MEM_FREE=$(free -m | grep "Mem" | awk '{print $7}')

function check_user() {
  echo "[INFO] start to check execution user"
  user=${USER}
  if [ "$user" = "pkgshipuser" ]; then
    echo "[INFO] check execution user ok"
  else
    echo "[ERROR] current user is not pkgshipuser,Please switch user to pkgshipuser."
    exit 1
  fi
}

function check_input() {
  if [ -z ${OPERATION} ] || [ ${OPERATION} != "start" -a ${OPERATION} != "stop" ]; then
    echo "[ERROR] input parameter error, usages: pkgshipd start|stop [selfpkg]"
    exit 1
  fi
}

function check_memory() {
  echo "[INFO] start to check memory"
  if [ ${OPERATION} = "start" ]; then
    if [ $MEM_FREE -lt $MEM_THRESHOLD ]; then
      echo "[ERROR] pkgship tool does not support memory less than ${MEM_THRESHOLD} MB."
      exit 1
    fi
  fi
  echo "[INFO] check memory ok"
}

function check_config_file() {
  echo "[INFO] start to check config file"
  # validate package.ini is exist
  if [ ! -f "$SYS_PATH/package.ini" ]; then
    echo "[ERROR] $SYS_PATH/package.ini dose not exist!!!"
    exit 1
  fi

  # check and create uwsgi out path
  if [ ! -d "$OUT_PATH" ]; then
    mkdir -p $OUT_PATH
    chown pkgshipuser: -R $OUT_PATH
  fi
  echo "[INFO] check config file ok"
}

function check_config_param() {
  echo "[INFO] start to check validation of config parameter."
  check_null
  # check query port and ip
  echo "[INFO] Check the IP and port of the service query"
  query_port=$(get_config "query_port")
  query_ip_addr=$(get_config "query_ip_addr")
  if [ -z "${query_port}" ] || [ -z "${query_ip_addr}" ]; then
    echo "[ERROR] CAN NOT find config name 'query_port' or 'query_ip_addr' in: $SYS_PATH/package.ini,
    Please check the file."
    exit 1
  fi
  check_addr $query_ip_addr $query_port
  echo "[INFO] IP addresses are all valid."

  echo "[INFO] Check validation of numbers."
  num_vars=(buffer-size http-timeout harakiri)
  for var in "${num_vars[@]}"; do
    value=$(get_config $var)
    if [[ -z "$value" ]]; then
      echo "[ERROR] CAN NOT find config name $var in: $SYS_PATH/package.ini, Please check the file."
      exit 1
    fi
    check_num ${value} ${var}
  done
  echo "[INFO] All numbers are valid."

  echo "[INFO] Check validation of words."
  log_level=$(get_config "log_level")
  check_word "log_level" "INFO|DEBUG|WARNING|ERROR|CRITICAL" $log_level
  echo "[INFO] All words are valid."

  check_config_path "temporary_directory" "d"
  check_config_path "log_path" "d"
  check_config_path "daemonize" "f"

  echo "[INFO] check config parameter ok."
}

function check_addr() {
  ip=$1
  port=$2
  ret=1
  # check ip
  if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
    ip=(${ip//\./ })
    [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
    ret=$?
  fi
  if [ $ret -ne 0 ]; then
    echo "[ERROR] Invalid ip of $ip"
    exit 1
  fi
  # check port
  check_num ${port-"port"} "port"
  if [[ $port -gt 65534 || $port -lt 1025 ]]; then
    echo "[ERROR] Invalid port of $port"
    exit 1
  fi
}

function check_logrotate_config() {
  max_size=$(get_config "max_bytes")
  max_count=$(get_config "backup_count")
  if [ -z "${max_size}" ] || [ -z "${max_count}" ]; then
    echo "[ERROR] Configuration parameters 'max_bytes' or 'backup_count' do not exist. Please confirm that these two parameters exist in configuration file 'package.ini'."
    exit 1
  fi
  check_num "${max_size}" "max_bytes"
  check_num "${max_count}" "backup_count"
  if [ ${max_size} -le 0 ] || [ ${max_count} -le 0 ]; then
    echo "[ERROR] The values of configuration parameters 'max_bytes' or 'backup_count' are less than or equal to 0, please make sure they are greater than 0"
    exit 1
  fi
}

function check_null() {
  list=$(cat $SYS_PATH/package.ini | grep -E ^[a-z_-]+= | awk -F '=' '{if($2 == "")print $1}')
  num=0
  for val in $list; do
    num=$(($num + 1))
  done
  if [ $num -gt 0 ]; then
    echo "[ERROR] The value of below config names is None in: $SYS_PATH/package.ini, Please check these parameters:"
    for val in $list; do
      echo $val
    done
    exit 1
  fi
}

function check_num() {
  result=$(echo $1 | grep '^[[:digit:]]*$')
  if [ -z "${result}" ]; then
    echo "[ERROR] $2 should be a number,please check this parameter "
    exit 1
  fi
}

function check_word() {
  if [ -z $3 ]; then
    echo "[ERROR] The value of below config names is None in: $SYS_PATH/package.ini, Please check these parameters: $1"
    exit 1
  fi

  result=$(echo $3 | grep -wE "$2")
  if [ -z "${result}" ]; then
    echo "[ERROR] $1 should be $2."
    exit 1
  fi
}

function check_config_path() {
  path_name=$1
  path_value=$(get_config "${path_name}")
  type=$2
  # value is none
  if [ -z "${path_value}" ]; then
    echo "[ERROR] The parameter '${path_name}' does not exist or has no value in the configuration file '$SYS_PATH/package.ini'"
    exit 1
  fi
  # file path is not exist,try create it
  if [ ! -e "${path_value}" ]; then
    if [ "${type}" = "d" ]; then
      creat_result=$(mkdir "${path_value}" 2>&1)
    fi
    if [ "${type}" = "f" ]; then
      creat_result=$(touch "${path_value}" 2>&1)
    fi
    if [[ "${creat_result}" =~ "Permission denied" ]]; then
      echo "[ERROR] The file path corresponding to the parameter '${path_name}' does not exist and cannot create, please modify the configuration file '$SYS_PATH/package.ini'"
      exit 1
    fi
    chown pkgshipuser: -R "${path_value}" >/dev/null 2>&1
  fi
  # value is a path
  if [ "${type}" = "d" ]; then
    create_try_result=$(touch ${path_value}/pkgship_create_test 2>&1)
    if [[ "${create_try_result}" =~ "Permission denied" ]]; then
      echo "[ERROR] parameter '${path_name}' corresponding path authority is too high,please ensure that 'pkgshipuser' users can read and write"
      exit 1
    fi
    rm -rf ${path_value}/pkgship_create_test
  else
    # value is a file
    if [ ! -w "${path_value}" ]; then
      echo "[ERROR] the file of the parameter '${path_name}' is not exist or permission is too high"
      echo "[ERROR] please ensure that exists and 'pkgshipuser' users can read and write"
      exit 1
    fi
  fi

}

function get_config() {
  cat $SYS_PATH/package.ini | grep -E ^$1 | sed 's/[[:space:]]//g' | awk 'BEGIN{FS="="}{print $2}'
}

function create_config_file() {
  echo "[INFO] start to create uwsgi file"
  query_port=$(get_config "query_port")
  query_ip_addr=$(get_config "query_ip_addr")
  daemonize=$(get_config "daemonize")
  buffer_size=$(get_config "buffer-size")
  http_timeout=$(get_config "http-timeout")
  harakiri=$(get_config "harakiri")
  uwsgi_file_path=$(find /usr/lib/ -name "packageship" | head -n 1)
  echo "[INFO] run packageship under path: $uwsgi_file_path"

  if [[ -z "$daemonize" ]] || [[ -z "$buffer_size" ]] || [[ -z "$http_timeout" ]] || [[ -z "$harakiri" ]]; then
    echo "[ERROR] CAN NOT find  all config name in: $SYS_PATH/package.ini, Please check the file."
    echo "[ERROR] The following config name is needed: daemonize, buffer_size, harakiri and http-timeout."
    exit 1
  fi
  if [ -z "$uwsgi_file_path" ]; then
    echo "[ERROR] CAN NOT find the uwsgi file path under: /usr/lib/"
    exit 1
  fi

  echo "[INFO] ${PKGSHIP_CONSTANT}.ini is saved to: $OUT_PATH/${PKGSHIP_CONSTANT}.ini"
  echo "[uwsgi]
http=$query_ip_addr:$query_port
module=packageship.selfpkg
uwsgi-file=$uwsgi_file_path/selfpkg.py
callable=app
buffer-size=$buffer_size
pidfile=$OUT_PATH/${PKGSHIP_CONSTANT}.pid
http-timeout=$http_timeout
harakiri=$harakiri
enable-threads=true
touch-logreopen=/opt/pkgship/uwsgi/.touch_for_logrotate
log-format = [remote url: %(uri)  %(method)]---{%(user_ip)} --->[%(user)]-->[%(addr)] -->[%(proto)] -->[%(host)] {%(vars) vars in %(pktsize) bytes} [%(ctime)]=> %(status)))
daemonize=$daemonize" >$OUT_PATH/${PKGSHIP_CONSTANT}.ini
  chown pkgshipuser: $OUT_PATH/${PKGSHIP_CONSTANT}.ini
  chmod 750 $OUT_PATH/${PKGSHIP_CONSTANT}.ini
  echo "[INFO] create uwsgi file ok"

  # create log_info file
  echo "[INFO] start to create log_info file"
  log_file_path=$(get_config "log_path")
  log_file=${log_file_path}/"log_info.log"
  if [ ! -e "${log_file}" ]; then
    touch "${log_file}"
    chmod 644 "${log_file}"
  else
    log_info=$(ls -al "${log_file}")
    if [[ ! "${log_info}" =~ "pkgshipuser" ]]; then
      echo "[ERROR] The owner of the ${log_file} is incorrect,please make sure the owner is pkgshipuser"
      exit 1
    fi
  fi
  echo "[INFO] create log_info file success"
}

function uwsgi_log_logrotate() {
  echo "[INFO] Start the logrotate task"
  uwsgi_log_process=$(ps -ef | grep uwsgi_logrotate.sh | grep -v grep | awk '{print $2}')
  if [ -n "${uwsgi_log_process}" ]; then
    echo "[WARNING] Task already exists, restart"
    ps -ef | grep uwsgi_logrotate.sh | grep -v grep | awk '{print $2}' | xargs kill -9 >/dev/null 2>&1
  fi
  # uwsgi log file logrotate
  uwsgi_log_file=$(get_config "daemonize")
  /bin/bash /etc/pkgship/uwsgi_logrotate.sh "${uwsgi_log_file}" &
  echo "[INFO] Start the logrotate task success"
}

function is_started() {
  pkgship_version=$(pkgship -v)
  if [[ -n ${pkgship_version} ]] && [[ ${pkgship_version} =~ "Version" ]]; then
    return 0
  else
    return 1
  fi
}

function start_service() {
  if is_started; then
    echo "[ERROR] ${PKGSHIP_CONSTANT} service is running, please stop it first."
    exit 1
  else
    uwsgi -d --ini $OUT_PATH/${PKGSHIP_CONSTANT}.ini
    echo "[INFO] START uwsgi service: ${PKGSHIP_CONSTANT}.ini"

    for i in {1..5}; do
      if is_started; then
        uwsgi_log_logrotate
        echo "[INFO] Start pkgship service success!!!"
        exit 0
      fi
      sleep 1s
    done

    echo "[ERROR] Service failed to start, please check log $(get_config "daemonize")"
    exit 1
  fi
}

function stop_service() {
  if [ ! -f "$OUT_PATH/${PKGSHIP_CONSTANT}.pid" ]; then
    echo "[ERROR] STOP service FAILED, $OUT_PATH/${PKGSHIP_CONSTANT}.pid dose not exist."
    echo "[ERROR] Please stop it manually by using [ps -aux] and [uwsgi --stop #PID]"
    exit 1
  fi

  if is_started; then
    uwsgi --stop $OUT_PATH/${PKGSHIP_CONSTANT}.pid
    echo "[INFO] STOP uwsgi service: $OUT_PATH/${PKGSHIP_CONSTANT}.ini"
    echo "[INFO] The run log is saved into: $(get_config "daemonize")"
    # stop uwsgi log file logrotate
    ps -ef | grep uwsgi_logrotate.sh | grep -v grep | awk '{print $2}' | xargs kill -9 >/dev/null 2>&1
    exit 0
  else
    echo "[ERROR] STOP service failed, Please start the service first."
    exit 1
  fi
}

function start_or_stop_service() {
  if [ "${OPERATION}" = "start" ]; then
    start_service
  elif [ "${OPERATION}" = "stop" ]; then
    stop_service
  fi
}

function check_redis_installed() {
  echo "[INFO] Redis check start"
  # check package.ini config
  redis_ip=$(get_config "redis_host")
  redis_port=$(get_config "redis_port")
  if [ -z "${redis_ip}" ] || [ -z "${redis_port}" ]; then
    echo "[Warning] Can not find config name 'redis_host' or 'redis_port' in: $SYS_PATH/package.ini,Please check the file"
    echo "If you have installed REDIS, Please set correct 'redis_host' and 'redis_port' values and re-execute the startup script "
    echo "If you have not installed REDIS, Please set 'redis_host: 127.0.0.1' and 'redis_port: 9200' and execute the automatic installation script '${SYS_PATH}/auto_install_pkgship_requires.sh' under the root user"
  fi

  check_num "${redis_port}" "redis_port"
  redis_max_connections=$(get_config "redis_max_connections")
  check_num "${redis_max_connections}" "redis_max_connections"
  # check whether to install Redis
  python_command="import redis
try:
    response = redis.Redis(host='$redis_ip',
                           port="$redis_port")
    print(response.ping())
except Exception:
    print(False)"
  python3_version=$(python3 --version)
  if [[ "${python3_version}" =~ "command not found" ]]; then
    echo "[ERROR] python3 command is not available,please install python3"
    exit 1
  fi

  result=$(python3 -c "$python_command")
  if [ "${result}" = "False" ]; then
    echo "========================================================================="
    echo "[WARNING] Redis connection FAILED,the following reason may cause failed:"
    echo "[INFO] 1. not installed Redis,if you need to install, please execute the automatic installation script '${SYS_PATH}/auto_install_pkgship_requires.sh' under the root user"
    echo "[INFO] 2. Redis service not started,please start redis service"
    echo "[INFO] 3. the Redis configuration is incorrect,please update value of 'redis_host' and 'redis_port' in ${SYS_PATH}/package.ini"
    echo "[INFO] 4. The Redis you connected is set to access with a password and cannot be used,please cancel the password or connect another Redis without a password"
    echo "=========================================================================="
    echo "[WARNING] redis is unavailable and the installation process continues, which may cause slow queries."
  else
    echo "[INFO] Redis check ok"
  fi

}

function check_es_installed() {
  echo "[INFO] Elasticsearch check start"
  # check package.ini config
  es_ip=$(get_config "database_host")
  es_port=$(get_config "database_port")
  if [ -z "${es_ip}" ] || [ -z "${es_port}" ]; then
    echo "[ERROR] Can not find config name 'database_host' or 'database_port' in: $SYS_PATH/package.ini,Please check the file"
    echo "If you have installed ES, Please set correct 'database_host' value and 'database_port' value and re-execute 'pkgshipd start' "
    echo "If you have not installed ES, please execute the automatic installation script '${SYS_PATH}/auto_install_pkgship_requires.sh' under the root user "
    exit 1
  fi
  check_num "${es_port}" "database_port"
  # check whether to install Elasticsearch
  visit_es_response=$(curl -s -XGET http://"${es_ip}":"${es_port}")
  if [ -z "${visit_es_response}" ]; then
    echo "========================================================================="
    echo "[ERROR] Elasticsearch connection FAILED,the following reason may cause failed:"
    echo "[INFO] 1. You have not installed Elasticsearch,If you need to install, please execute the automatic installation script '${SYS_PATH}/auto_install_pkgship_requires.sh' under the root user"
    echo "[INFO] 2. Elasticsearch configuration is incorrect,please update value of 'database_host' and 'database_port' in $SYS_PATH/package.ini"
    echo "[INFO] 3. Elasticsearch service not started,please start elasticsearch service."
    exit 1
  fi
  # check whether a password is set
  if [[ $visit_es_response =~ "authentication" ]]; then
    echo "[ERROR] The ES you installed is set to access with a password and cannot be used."
    echo "Please cancel the password or install another ES without a password"
    exit 1
  fi
  # check elasticsearch version
  es_version=$(echo "${visit_es_response}" | grep "number" | awk -F'"' '{print $4}')
  if [ "${es_version}" != "7.10.1" ]; then
    echo "[ERROR] The Elasticsearch version you installed is not the recommended version 7.10.1,which may cause it to be unusable."
    exit 1
  fi

  echo "[INFO] Elasticsearch check ok"
}

function main() {
  #check user
  check_user
  # check input
  check_input
  # check free memory
  check_memory
  # check uwsgi and package.ini
  check_config_file
  if [ "${OPERATION}" = "start" ]; then
    # check config parameter
    check_config_param
    # check logrotate config
    check_logrotate_config
    # create uwsgi files
    create_config_file
    # check redis installed
    check_redis_installed
    # check es installed
    check_es_installed
  fi
  #star or stop service
  start_or_stop_service
}

main
