#!/bin/bash
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------
set -u

#==============================================================================
# Declarations and Environment

# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/dev_c2s/Server/Unix/p4/common/bin/p4sanity_check.sh#2 $ $Change: 31472 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}
[[ "$VersionStream" == r* ]] || Version="${Version^^}"

declare ThisScript=${0##*}
declare ThisUser=
declare ThisHost=${HOSTNAME%%.*}
declare CmdLine="$0 $*"
declare UserP4CONFIG=
declare SDPInstance=
declare -i ErrorCount=0
declare Cmd=
declare CmdLog=

#==============================================================================
# Local Functions

function msg () { echo -e "$*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "$ErrorCount"; }

#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style, either -h (for short form) or -man (for man-page like format).
# The default is -h.
#
# $2 - error message (optional).  Specify this if usage() is called due to
# user error, in which case the given message displayed first, followed by the
# standard usage message (short or long depending on $1).  If displaying an
# error, usually $1 should be -h so that the longer usage message doesn't
# obscure the error message.
#
# Sample Usage:
# usage 
# usage -man
# usage -h "Incorrect command line usage."
#
# This last example generates a usage error message followed by the short
# '-h' usage summary.
#------------------------------------------------------------------------------
function usage
{
   declare style=${1:--h}
   declare errorMessage=${2:-Unset}

   if [[ $errorMessage != Unset ]]; then
      msg "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n" >&2
   fi

   msg "USAGE for $ThisScript version $Version:

$ThisScript [<instance>] [-p4config <PathToFile>]

   or

$ThisScript -h|-man
"
   if [[ $style == -man ]]; then
      msg "DESCRIPTION:
	This script does basic sanity checks for a p4d service:

	To pass the sanity checks, the service must:
	* Respond without error to 'p4 set'
	* Respond without error to 'p4 -s info'
	* Respond without error to 'p4 -s changes -t -m 10' and display at least one changelist.

HELP OPTIONS:
 -h	Display short help message
 -man	Display man-style help message

OPTIONS:
 -p4config <PathToFile>
	Use the '-p4config' option use this SDP p4sanity_check.sh script to check
	an arbitrary p4d server.  That arbitrary server can be any p4d version,
	operating on any platform, and need not be managed with SDP.

	To use this option, first create a P4CONFIG file that defines settings
	needed to access the other server.  As a convention, identify a short tag
	name for the other server to use in the P4CONFIG file.  In the example
	below, we use 'mot' for \"my other server\".  Create a P4CONFIG file text
	named /p4/common/site/config/.p4config.mot that contains these settings:

	P4PORT=ssl:my_other_server:1666
	P4USER=p4admin
	P4TICKETS=/p4/common/site/config/.p4tickets.mot
	P4TRUST=/p4/common/site/config/.p4trust.mot

	The P4TRUST setting is only needed if the port is SSL-enabled. If it
	is enabled, next trust the port:

	p4 -E P4CONFIG=/p4/common/site/config/.p4config.mot trust -y

	Next, generate a ticket on that connection:
	p4 -E P4CONFIG=/p4/common/site/config/.p4config.mot login -a

	Provide the password if prompted.

	Finally, call p4sanity_check.sh and specify the config file:

 	p4sanity_check.sh -p4config /p4/common/site/config/.p4config.mot

EXIT CODES:
	An exit code of 0 indicates all sanity checks passed.  Otherwise, a
	non-zero exit code is returned.
"
   fi

   exit 1
}


#==============================================================================
# Command Line Processing

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case "$1" in
      (-h) usage -h;;
      (-man) usage -man;;
      (-p4config) UserP4CONFIG="$2"; shiftArgs=1;;
      (-*) usage -h "Unknown option ($1).";;
      (*)
         # The first positional parameter is interpreted as the SDP Instance.
         if [[ -z "$SDPInstance" ]]; then
            SDPInstance="$1"
         else
            usage -h "SDP Instance already specified as $SDPInstance; addtional parameter ($1) is invalid."
         fi
      ;;
   esac

   # Shift (modify $#) the appropriate number of times.
   shift; while [[ $shiftArgs -gt 0 ]]; do
      [[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
      shiftArgs=$shiftArgs-1
      shift
   done
done
set -u

#==============================================================================
# Command Line Verification

# If SDPInstance parameter was not provided, default to $SDP_INSTANCE if set.
[[ -n "$SDPInstance" ]] || SDPInstance="${SDP_INSTANCE:-}"
[[ -n "$SDPInstance" ]] || \
   usage -h "Missing <instance> parameter. The <instance> must be given as a parameter to this script, or else the \$SDP_INSTANCE environment variable defined.  It can be set by doing:\\n\\n\\tsource /p4/common/bin/p4_vars <instance>\\n\\nor by passing in the instance name as a parameter to this script.\\n"

#==============================================================================
# Main Program

ThisUser=$(id -n -u)
msg "Started $ThisScript version $Version as $ThisUser@$ThisHost on $(date +'%a %Y-%m-%d %H:%M:%S %Z') as:\\n$CmdLine"
CmdLog=$(mktemp "$LOGS/tmp.XXXX.log")

# shellcheck disable=SC1091
source /p4/common/bin/p4_vars "$SDPInstance" ||\
   bail "Could not do: source /p4/common/bin/p4_vars \"$SDPInstance\""

# If the -p4config option is used, handle it here, after the standard SDP shell environment
# has been sourced (above).
if [[ -n "$UserP4CONFIG" ]]; then
   export P4CONFIG="$UserP4CONFIG"
   P4DVersionString=$($P4BIN -ztag -F %serverVersion% info -s 2>/dev/null)
   [[ -n "$P4DVersionString" ]] ||\
      usage -h "Could not determine version using P4PORT in user-specified P4CONFIG file [$UserP4CONFIG]."
   P4DMajorVersion=$(echo "$P4DVersionString" | cut -d / -f 3)
   P4DBuild=$(echo "$P4DVersionString" | cut -d / -f 4 | cut -d ' ' -f 1)
   export P4D_VERSION="${P4DMajorVersion}.${P4DBuild}"
fi

if [[ -n "$UserP4CONFIG" ]]; then
   P4="$P4BIN -E P4CONFIG=$UserP4CONFIG"
else
   P4="$P4BIN -p $P4PORT -u $P4USER"
fi

Cmd="$P4 set"
msg "\\nPerforce settings checked with '$Cmd':"
$Cmd || errmsg "Could not do: $Cmd"

Cmd="$P4 -s info"
msg "Perforce server info checked with: $Cmd"
$Cmd || errmsg "p4d is not reachable with the given settings."

Cmd="$P4 -s changes -t -m 10"
msg "\\nScanning recent changelists.\\nEnsure output below from '$Cmd' looks reasonable:\\n"
$Cmd 2>&1 | tee "$CmdLog" || errmsg "Could not list recent changelists."

grep -q '^info: Change' "$CmdLog" ||\
   errmsg "No changelists detecgted in the output."

rm -f "$CmdLog"

if [[ "$ErrorCount" -eq 0 ]]; then
   msg "\\nPASS: Sanity checks look OK."
else
   msg "\\nFAIL: Errors reported doing sanity checks."
fi

exit "$ErrorCount"
