#!/bin/bash

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

usage() {
  [ $# -eq 0 ] || echo "$@"
  echo "
usage: $0 [options] command [command-arg] [command [command-arg]] ...

Options:
    --solr solr_uri
    --zk   zk_ensemble
    --help
    --quiet

Commands:
    init        [--force]

    instancedir [--generate path]
                [--create name path]
                [--update name path]
                [--get name path]
                [--delete name]
                [--list]

    collection  [--create name -s <numShards>
                              [-c <collection.configName>]
                              [-r <replicationFactor>]
                              [-m <maxShardsPerNode>]
                              [-n <createNodeSet>]]
                [--delete name]
                [--reload name]
                [--stat name]
                [--deletedocs name]
                [--list]
                [--config name <json>]

    core        [--create name [-p name=value]...]
                [--reload name]
                [--unload name]
                [--status name]
  "
  exit 1
}

die() {
  echo "$1"
  exit 1
}

# FIXME: this is here only for testing purposes
local_coreconfig() {
  case "$2" in
    put)
      echo "$4" > "/var/lib/solr/$3"
      ;;
    list)
      ls -d /var/lib/solr/*/conf 2>/dev/null | sed -e 's#var/lib/solr#configs#'
      ;;
    clear)
      if [ "$3" != "/" ] ; then
        sudo -u solr rm -rf /var/lib/solr/`basename $3`/* 2>/dev/null
      fi
      ;;
    upconfig)
      # the following trick gets us around permission issues
      rm -rf /tmp/solr_conf.$$
      cp -r $4 /tmp/solr_conf.$$
      chmod o+r -R /tmp/solr_conf.$$
      sudo -u solr bash -c "mkdir /var/lib/solr/$6 2>/dev/null ; cp -r /tmp/solr_conf.$$ /var/lib/solr/$6/conf"
      RES=$?
      rm -rf /tmp/solr_conf.$$
      return $RES
      ;;
    downconfig)
      mkdir -p $4
      cp -r /var/lib/solr/$6/conf/* $4
      ;;
  esac
}

solr_webapi() {
  # If SOLR_ADMIN_URI wasn't given explicitly via --solr we need to guess it
  if [ -z "$SOLR_ADMIN_URI" ] ; then
    for node in `get_solr_state | sed -ne 's#/live_nodes/\(.*:[0-9][0-9]*\).*$#\1#p'` localhost:$SOLR_PORT ; do
      if $SOLR_ADMIN_CURL "http://$node/solr" >/dev/null 2>&1 ; then
        SOLR_ADMIN_URI="http://$node/solr"
        break
      fi
    done
    [ -n "$SOLR_ADMIN_URI" ] || die "Error: can't discover Solr URI. Please specify it explicitly via --solr."
  fi
  URI="$SOLR_ADMIN_URI$1"
  shift
  local WEB_OUT=$($SOLR_ADMIN_CURL $URI "$@" | sed -e 's#>#>\n#g')
  if [ $? -eq 0 ] && (echo "$WEB_OUT" | grep -q 'HTTP/.*200.*OK') ; then
    echo "$WEB_OUT" | egrep -q '<lst name="(failure|exception|error)">' || return 0
  fi

  die "Error: A call to SolrCloud WEB APIs failed: $WEB_OUT"
}

get_solr_state() {
  if [ -z "$SOLR_STATE" ] ; then
    SOLR_STATE=`eval $SOLR_ADMIN_ZK_CMD -cmd list 2>/dev/null`
  fi

  echo "$SOLR_STATE" | grep -v '^/ '
}

SOLR_CONF_DIR=${SOLR_CONF_DIR:-/etc/solr/conf}

if [ -e "$SOLR_CONF_DIR/solr-env.sh" ] ; then
  . "$SOLR_CONF_DIR/solr-env.sh"
elif [ -e /etc/default/solr ] ; then
  . /etc/default/solr
fi

SOLR_PORT=${SOLR_PORT:-8983}
SOLR_ADMIN_CURL='curl -i --retry 5 -s -L -k --negotiate -u :'
SOLR_ADMIN_CHAT=echo
SOLR_ADMIN_API_CMD='solr_webapi'

SOLR_INSTALL_DIR=/usr/odp/current/solr

# Autodetect JAVA_HOME if not defined
. /usr/lib/bigtop-utils/bigtop-detect-javahome

# First eat up all the global options

while test $# != 0 ; do
  case "$1" in
    --help)
      usage
      ;;
    --quiet)
      SOLR_ADMIN_CHAT=/bin/true
      shift 1
      ;;
    --solr)
      [ $# -gt 1 ] || usage "Error: $1 requires an argument"
      SOLR_ADMIN_URI="$2"
      shift 2
      ;;
    --zk)
      [ $# -gt 1 ] || usage "Error: $1 requires an argument"
      SOLR_ZK_ENSEMBLE="$2"
      shift 2
      ;;
    *)
      break
      ;;
  esac
done

if [ -z "$SOLR_ZK_ENSEMBLE" ] ; then
  SOLR_ADMIN_ZK_CMD="local_coreconfig"
  cat >&2 <<-__EOT__
	Warning: Non-SolrCloud mode has been completely deprecated
	Please configure SolrCloud via SOLR_ZK_ENSEMBLE setting in
	/etc/default/solr
	If you running remotely, please use --zk zk_ensemble.
	__EOT__
else
  SOLR_ADMIN_ZK_CMD='${JAVA_HOME}/bin/java -classpath "${SOLR_INSTALL_DIR}/server/solr-webapp/webapp/WEB-INF/lib/*:${SOLR_INSTALL_DIR}/server/lib/ext/*:${SOLR_INSTALL_DIR}/server/lib/*" org.apache.solr.cloud.ZkCLI -zkhost $SOLR_ZK_ENSEMBLE 2>/dev/null'
fi


# Now start parsing commands -- there has to be at least one!
[ $# -gt 0 ] || usage
while test $# != 0 ; do
  case "$1" in
    debug-dump)
      get_solr_state

      shift 1
      ;;

    init)
      if [ "$2" == "--force" ] ; then
        shift 1
      else
        LIVE_NODES=`get_solr_state | sed -ne 's#/live_nodes/##p'`

        if [ -n "$LIVE_NODES" ] ; then
          die "Warning: It appears you have live SolrCloud nodes running: `printf '\n%s\nPlease shut them down.' \"${LIVE_NODES}\"`"
        elif [ -n "`get_solr_state`" ] ; then
          die "Warning: Solr appears to be initialized (use --force to override)"
        fi
      fi

      eval $SOLR_ADMIN_ZK_CMD -cmd makepath / > /dev/null 2>&1 || :
      eval $SOLR_ADMIN_ZK_CMD -cmd clear /    || die "Error: failed to initialize Solr"

      eval $SOLR_ADMIN_ZK_CMD -cmd put /solr.xml "'$(cat $SOLR_INSTALL_DIR/server/solr/solr.xml)'"

      shift 1
      ;;

    coreconfig)
      $SOLR_ADMIN_CHAT  "Warning: coreconfig is deprecated, please use instancedir instead (consult documentation on differences in behaviour)."
      shift 1
      set instancedir "$@"
      ;;
    instancedir)
      [ $# -gt 1 ] || usage "Error: incorrect specification of arguments for $1"
      case "$2" in
        --create)
            [ $# -gt 3 ] || usage "Error: incorrect specification of arguments for $1 $2"

            if [ -d $4/conf ] ; then
              INSTANCE_DIR="$4/conf"
            else
              INSTANCE_DIR="$4"
            fi

            [ -e ${INSTANCE_DIR}/solrconfig.xml -a -e ${INSTANCE_DIR}/managed-schema ] || die "Error: ${INSTANCE_DIR} must be a directory with at least solrconfig.xml and managed-schema"

            get_solr_state | grep -q '^ */configs/'"$3/" && die "Error: \"$3\" configuration already exists. Aborting. Try --update if you want to override"

            $SOLR_ADMIN_CHAT "Uploading configs from ${INSTANCE_DIR} to $SOLR_ZK_ENSEMBLE. This may take up to a minute."
            eval $SOLR_ADMIN_ZK_CMD -cmd upconfig -confdir ${INSTANCE_DIR} -confname $3 2>/dev/null || die "Error: can't upload configuration"
            shift 4
            ;;
        --update)
            [ $# -gt 3 ] || usage "Error: incorrect specification of arguments for $1 $2"

            if [ -d $4/conf ] ; then
              INSTANCE_DIR="$4/conf"
            else
              INSTANCE_DIR="$4"
            fi

            [ -e ${INSTANCE_DIR}/solrconfig.xml -a -e ${INSTANCE_DIR}/managed-schema ] || die "Error: ${INSTANCE_DIR} must be a directory with at least solrconfig.xml and managed-schema"

            eval $SOLR_ADMIN_ZK_CMD -cmd clear /configs/$3 2>/dev/null || die "Error: can't delete configuration"

            $SOLR_ADMIN_CHAT "Uploading configs from ${INSTANCE_DIR} to $SOLR_ZK_ENSEMBLE. This may take up to a minute."
            eval $SOLR_ADMIN_ZK_CMD -cmd upconfig -confdir ${INSTANCE_DIR} -confname $3 2>/dev/null || die "Error: can't upload configuration"
            shift 4
            ;;
        --get)
            [ $# -gt 3 ] || usage "Error: incorrect specification of arguments for $1 $2"

            [ -e "$4" ] && die "Error: subdirectory $4 already exists"

            $SOLR_ADMIN_CHAT "Downloading configs from $SOLR_ZK_ENSEMBLE to $4. This may take up to a minute."
            eval $SOLR_ADMIN_ZK_CMD -cmd downconfig -confdir "$4/conf" -confname "$3" 2>/dev/null || die "Error: can't download configuration"
            shift 4
            ;;
        --delete)
            [ $# -gt 2 ] || usage "Error: incorrect specification of arguments for $1"

            eval $SOLR_ADMIN_ZK_CMD -cmd clear /configs/$3 2>/dev/null || die "Error: can't delete configuration"
            shift 3
            ;;
        --generate)
            [ $# -gt 2 ] || usage "Error: incorrect specification of arguments for $1"

            [ -e "$3" ] && die "Error: subdirectory $3 already exists"

            mkdir -p "$3" > /dev/null 2>&1
            [ -d "$3" ] || usage "Error: $3 has to be a directory"
            # sample_techproducts_configs is used as default, this provides many common used optional features
            cp -r ${SOLR_INSTALL_DIR}/server/solr/configsets/sample_techproducts_configs/conf "$3/conf"
            shift 3
            ;;
        --list)
            get_solr_state | sed -n -e '/\/configs\//s#^.*/configs/\([^/]*\)/.*$#\1#p' | sort -u
            shift 2
            ;;
        *)
            shift 1
            ;;
      esac
      ;;

    collection)
      [ "$2" = "--list" ] || [ $# -gt 2 ] || usage "Error: incorrect specification of arguments for $1"
      case "$2" in
        --create)
            COL_CREATE_NAME=$3
            COL_CREATE_NUMSHARDS=1
            shift 3
            while test $# -gt 0 ; do
              case "$1" in
                -s)
                  [ $# -gt 1 ] || usage "Error: collection --create name $1 requires an argument"
                  COL_CREATE_NUMSHARDS="$2"
                  shift 2
                  ;;
                -c)
                  [ $# -gt 1 ] || usage "Error: collection --create name $1 requires an argument"
                  COL_CREATE_CONFNAME="$2"
                  shift 2
                  ;;
                -r)
                  [ $# -gt 1 ] || usage "Error: collection --create name $1 requires an argument"
                  COL_CREATE_REPL="$2"
                  shift 2
                  ;;
                -m)
                  [ $# -gt 1 ] || usage "Error: collection --create name $1 requires an argument"
                  COL_CREATE_MAXSHARDS="$2"
                  shift 2
                  ;;
                -n)
                  [ $# -gt 1 ] || usage "Error: collection --create name $1 requires an argument"
                  COL_CREATE_NODESET="$2"
                  shift 2
                  ;;
                 *)
                  break
                  ;;
              esac
            done

            COL_CREATE_CALL="&name=${COL_CREATE_NAME}"
            if [ "$COL_CREATE_NUMSHARDS" -gt 0 ] ; then
              COL_CREATE_CALL="${COL_CREATE_CALL}&numShards=${COL_CREATE_NUMSHARDS}"
            else
              usage "Error: collection --create name needs to have more than 0 shards specified"
            fi
            [ -n "$COL_CREATE_CONFNAME" ] && COL_CREATE_CALL="${COL_CREATE_CALL}&collection.configName=${COL_CREATE_CONFNAME}"
            [ -n "$COL_CREATE_REPL" ] && COL_CREATE_CALL="${COL_CREATE_CALL}&replicationFactor=${COL_CREATE_REPL}"
            [ -n "$COL_CREATE_MAXSHARDS" ] && COL_CREATE_CALL="${COL_CREATE_CALL}&maxShardsPerNode=${COL_CREATE_MAXSHARDS}"
            [ -n "$COL_CREATE_NODESET" ] && COL_CREATE_CALL="${COL_CREATE_CALL}&createNodeSet=${COL_CREATE_NODESET}"

            eval $SOLR_ADMIN_API_CMD "'/admin/collections?action=CREATE${COL_CREATE_CALL}'"

            shift 4
            ;;
        --config)
            eval $SOLR_ADMIN_API_CMD "'/$3/config'" -H "'Content-Type: application/json'" "--data-binary '$4'"
            shift 4
            ;;
        --delete|--reload)
            COL_ACTION=`echo $2 | tr '[a-z]-' '[A-Z] '`
            eval $SOLR_ADMIN_API_CMD "'/admin/collections?action=`echo $COL_ACTION`&name=$3'"
            shift 3
            ;;
        --deletedocs)
            eval $SOLR_ADMIN_API_CMD "'/$3/update?commit=true'" -H "'Content-Type: text/xml'" "--data-binary '<delete><query>*:*</query></delete>'"
            shift 3
            ;;
        --stat)
            get_solr_state | sed -ne '/\/collections\//s#^.*/collections/##p' | sed -ne '/election\//s###p' | grep "$3/"
            shift 3
            ;;
        --list)
            get_solr_state | sed -ne '/\/collections\/[^\/]*$/s#^.*/collections/##p'
            shift 2
            ;;
        *)
            shift 1
            ;;
      esac
      ;;

    core)
      [ $# -gt 2 ] || usage "Error: incorrect specification of arguments for $1"
      case "$2" in
        --create)
          CORE_CREATE_NAME="$3"
          shift 3
          while test $# -gt 0 ; do
            case "$1" in
              -p)
                [ $# -gt 1 ] || usage "Error: core --create name $1 requires an argument of key=value"
                CORE_KV_PAIRS="${CORE_KV_PAIRS}&${2}"
                shift 2
                ;;
               *)
                break
                ;;
            esac
          done
          [ -n "$CORE_KV_PAIRS" ] || CORE_KV_PAIRS="&instanceDir=${CORE_CREATE_NAME}"

          eval $SOLR_ADMIN_API_CMD "'/admin/cores?action=CREATE&name=${CORE_CREATE_NAME}${CORE_KV_PAIRS}'"
          ;;
        --reload|--unload|--status)
          CORE_ACTION=`echo $2 | tr '[a-z]-' '[A-Z] '`
          eval $SOLR_ADMIN_API_CMD "'/admin/cores?action=`echo $CORE_ACTION`&core=$3'"
          shift 3
          ;;
        *)
          shift 1
          ;;
      esac
      ;;

    *)
      usage "Error: unrecognized command $1"
      ;;
  esac
done

# If none of the above commands ended up calling die -- we're OK
exit 0
