#!/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
#------------------------------------------------------------------------------
#==============================================================================
# Declarations and Environment
# Allow override of P4U_HOME, which is set only when testing P4U scripts.
export P4U_HOME="${P4U_HOME:-/p4/common/bin}"
export P4U_LIB="${P4U_LIB:-/p4/common/lib}"
export P4U_ENV="$P4U_LIB/p4u_env.sh"
export VERBOSITY=${VERBOSITY:-3}
# Environment isolation. For stability and security reasons, prepend
# PATH to include dirs where known-good scripts exist.
# known/tested PATH and, by implication, executables on the PATH.
export PATH="$P4U_HOME:$PATH:~/bin:."
export P4CONFIG="${P4CONFIG:-.p4config}"
[[ -r "$P4U_ENV" ]] || {
echo -e "\\nError: Cannot load environment from: $P4U_ENV\\n\\n"
exit 1
}
declare BASH_LIBS=$P4U_ENV
BASH_LIBS+=" $P4U_LIB/libcore.sh"
BASH_LIBS+=" $P4U_LIB/libp4u.sh"
for bash_lib in $BASH_LIBS; do
# shellcheck disable=SC1090
source "$bash_lib" ||\
{ echo -e "\\nFATAL: Failed to load bash lib [$bash_lib]. Aborting.\\n"; exit 1; }
done
declare Version=1.1.1
export VERBOSITY=3
#==============================================================================
# Local Functions
#------------------------------------------------------------------------------
# Function: terminate
function terminate
{
# Disable signal trapping.
trap - EXIT SIGINT SIGTERM
vvmsg "$THISSCRIPT: EXITCODE: $OverallReturnStatus"
# Stop logging.
[[ "${P4U_LOG}" == off ]] || stoplog
# Don't litter.
cleanTrash
# With the trap removed, exit.
exit "$OverallReturnStatus"
}
#------------------------------------------------------------------------------
# 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
# errror, usually $1 should be -h so that the longer usage message doesn't
# obsure the error message.
#
# Sample Usage:
# usage
# usage -h
# usage -man
# usage -h "Incorrect command line usage."
#------------------------------------------------------------------------------
function usage
{
declare style=${1:--h}
declare errorMessage=${2:-Unset}
if [[ "$errorMessage" != Unset ]]; then
echo -e "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n"
fi
echo "USAGE for $THISSCRIPT v$Version:
$THISSCRIPT [-i <instance>] [-L <log>] [-v<n>] [-p|-n] [-D]
or
$THISSCRIPT [-h|-man|-V]
"
if [[ $style == -man ]]; then
echo -e "
DESCRIPTION:
This script obsoletes the SetDefaultDepotSpecMapField.py trigger.
It does so by following a series of steps. First, it ensures that
the configurable server.depot.root is set correctly, setting it
is not set.
Next, the Triggers table is checked to ensure the call to the
SetDefaultDepotSpecMapField.py is not called; it is deleted from
the Triggers table if found.
Last, it resets the 'Map:' field of depot specs for depot
types where that is appropriate, setting it to the default value of
'<DepotName>/...', so that it honors the server.depot.root
configruable. This is done for depots of these types:
* stream
* local
* spec
* unload
but not these:
* unload
* remote
* graph
If an unknown depot type is encountered, the Map field is reset
as well if it is set.
This script does a preflight check first, reporting any cases
where the starting conditions are not as expected, such as. These
are treated as Warnings:
* Configurable server.depot.root is already set
* SetDefaultDepotSpecMapField.py not found in triggers
* Depot already has Map field set to the default <DepotName>/...
These conditions are treated as Errors, and will abort processing:
* Depot Map field set to something other than the default.
* Configurable server.depot.root is set, but to something other
than what it should be.
OPTIONS:
-v<n> Set verbosity 1-5 (-v1 = quiet, -v5 = highest).
-L <log>
Specify the path to a log file, or the special value 'off' to disable
logging. By default, all output (stdout and stderr) goes to
EDITME_DEFAULT_LOG
NOTE: This script is self-logging. That is, output displayed on the screen
is simultaneously captured in the log file. Do not run this script with
redirection operators like '> log' or '2>&1', and do not use 'tee.'
-p Run preflight checks only, and then stop. By default, actual changes
occur if preflight checks find no issues.
-n No-Op. No actions are taken that would affect data significantly;
instead commands are displayed rather than executed.
-D Set extreme debugging verbosity.
HELP OPTIONS:
-h Display short help message
-man Display man-style help message
-V Dispay version info for this script and its libraries.
EXAMPLES:
A typical flow for this script is to do a preflight first, and then
a live run, for any given instance:
$THISSCRIPT -i 1 -p
$THISSCRIPT -i 1
Note that if using '-n', the '-v5' flag should also be used.
"
fi
exit 1
}
#==============================================================================
# Command Line Processing
declare SDPInstance=Unset
declare SDPEnvFile="/p4/common/bin/p4_vars"
declare SDPInstanceCfgFile=
declare -i PreflightOnly=0
declare -i RemoveOldTrigger=1
declare -i SetSDRConfigurable=1
declare -i shiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case $1 in
(-i) SDPInstance="$2"; shiftArgs=1;;
(-h) usage -h;;
(-man) usage -man;;
(-V) show_versions; exit 1;;
(-v1) export VERBOSITY=1;;
(-v2) export VERBOSITY=2;;
(-v3) export VERBOSITY=3;;
(-v4) export VERBOSITY=4;;
(-v5) export VERBOSITY=5;;
(-L) export P4U_LOG=$2; shiftArgs=1;;
(-p) PreflightOnly=1;;
(-n) export NO_OP=1;;
(-D) set -x;; # Debug; use 'set -x' mode.
(*) usage -h "Unknown arg ($1).";;
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 not set on the command line with -i, default SDPInstance to the
# $SDP_INSTANCE environment variable, if defined.
[[ "$SDPInstance" == "Unset" ]] && SDPInstance="${SDP_INSTANCE:-Unset}"
[[ "$SDPInstance" == "Unset" ]] && \
bail "The '-i <SDP_Instance>' argument is required unless \$SDP_INSTANCE is defined in the environment."
SDPInstanceCfgFile="/p4/common/config/p4_${SDPInstance}.vars"
[[ -r "$SDPInstanceCfgFile" ]] || \
bail "Missing SDP instance config file: $SDPInstanceCfgFile"
# Load the standard SDP shell environment for the given instance.
# shellcheck disable=SC1090
source "$SDPEnvFile" "$SDPInstance"
[[ "${P4U_LOG:-Unset}" == Unset ]] && \
P4U_LOG="${LOGS:-/tmp}/${THISSCRIPT%.sh}.$(date +'%Y%m%d-%H%M%S').log"
#==============================================================================
# Main Program
trap terminate EXIT SIGINT SIGTERM
declare -i OverallReturnStatus=0
declare DepotType=
declare Depot=
declare ActualMapFieldValue=
declare ExpectedMapFieldValue=
declare SDRExpectedValue="/p4/$SDPInstance/depots"
declare SDRActualValue=
declare TmpFile1="$P4TMP/tmp1.$$.$RANDOM"
declare TmpFile2="$P4TMP/tmp2.$$.$RANDOM"
declare TmpPrefix="$P4TMP/${THISSCRIPT/.sh/.tmp}"
declare -i ErrorCount=0
declare -i WarningCount=0
if [[ "${P4U_LOG}" != off ]]; then
touch "${P4U_LOG}" || bail "Couldn't touch log file [${P4U_LOG}]."
# Redirect stdout and stderr to a log file.
exec > >(tee "${P4U_LOG}")
exec 2>&1
initlog
fi
msg "Started $THISSCRIPT v$Version at $(date)."
msg "${H2}\\nPreflight Check for super access."
[[ "$("$P4BIN" protects -m)" == "super" ]] || \
bail "Super access for $P4USER could not be verified. Aborting."
msg "Super user access for user $P4USER verified."
msg "${H2}\\nPreflight Check for server.depot.root configurable."
SDRActualValue=$("$P4BIN" -ztag -F "%Value%" configure show server.depot.root)
if [[ -z "$SDRActualValue" ]]; then
msg "The configurable server.depot.root is not (yet) set, as expected."
elif [[ "$SDRActualValue" == "$SDRExpectedValue" ]]; then
warnmsg "The configurable server.depot.root is already set to the intended value."
WarningCount+=1
SetSDRConfigurable=0
else
errmsg "The configurable server.depot.root is set to an unexpected value [$SDRActualValue], expected value is [$SDRExpectedValue]."
WarningCount+=1
fi
msg "${H2}\\nPreflight Checks for Depot Spec Map Fields:"
# Note: graph depots aren't reported by 'p4 depots' with flags
# given here, so we don't need any special processing to ignore them
# (as server.depot.root does not apply to them).
for data in $("$P4BIN" -ztag -F "%name%:%type%" depots); do
vvmsg "d=[$data]"
[[ "$data" == *":"* ]] || bail "Unexpected output. Bailling."
Depot="${data%%:*}"
DepotType="${data##*:}"
vvmsg "D=[$Depot] T=[$DepotType]"
case "$DepotType" in
(archive|remote)
vmsg "Skipping depot $Depot of type $DepotType."
continue
;;
(stream|local|spec|unload)
vmsg "Processing depot $Depot of type $DepotType."
;;
(*)
warnmsg "Processing depot $Depot of UKNOWN type $DepotType."
WarningCount+=1
;;
esac
ExpectedMapFieldValue="/p4/$SDPInstance/depots/$Depot/..."
DefaultMapFieldValue="$Depot/..."
ActualMapFieldValue=$("$P4BIN" -ztag -F "%Map%" depot -o "$Depot")
if [[ "$ActualMapFieldValue" == "$DefaultMapFieldValue" ]]; then
warnmsg "Depot $Depot already has default map field value of $DefaultMapFieldValue."
WarningCount+=1
else
echo "$ActualMapFieldValue" > "$TmpFile1"
echo "$ExpectedMapFieldValue" > "$TmpFile2"
if ! diff -q "$TmpFile1" "$TmpFile2"; then
echo "DBG AV=[$ActualMapFieldValue]"
errmsg "Depot $Depot has unexpected map field value of: $ActualMapFieldValue"
ErrorCount+=1
else
msg "Depot $Depot has expected Map field value of: $ActualMapFieldValue"
fi
fi
done
msg "${H2}\\nPreflight Check for trigger script SetDefaultDepotSpecMapField.py."
"$P4BIN" triggers -o > "$TmpFile1"
if grep -i -q SetDefaultDepotSpecMapField.py "$TmpFile1"; then
msg "Trigger SetDefaultDepotSpecMapField.py detected, as expected."
else
warnmsg "Missing trigger SetDefaultDepotSpecMapField.py (which was about to be deleted anyway)."
WarningCount+=1
RemoveOldTrigger=0
fi
msg "Preflight checks discovered $ErrorCount errors and $WarningCount warnings."
[[ "$ErrorCount" -gt 0 ]] && \
bail "Preflivhg checkes FAILED.\\nAborting due to errors found in preflight checks."
msg "Preflight checks PASSED."
if [[ "$PreflightOnly" -eq 1 ]]; then
msg "Exiting after preflight checks due to '-p'."
exit "$OverallReturnStatus"
fi
msg "${H2}\\nUpdating configurable server.depot.root."
# Set the configurable server.depot.root. If this fails, we bail. This
# is the first change to actual data in our processing, an so we can
# bail here without foregoing idempotency.
if [[ "$SetSDRConfigurable" -eq 1 ]]; then
run "$P4BIN configure set server.depot.root=$SDRExpectedValue" \
"Setting configurable server.depot.root to: $SDRExpectedValue" ||\
bail "Failed to set server.depot.root configurable."
else
msg "Configurable server.depot.root is already set to the expected value."
fi
msg "${H2}\\nUpdating triggers table."
if [[ "$RemoveOldTrigger" -eq 1 ]]; then
"$P4BIN" triggers -o | grep -v '^#' | grep -i -v SetDefaultDepotSpecMapField.py > "$TmpFile1" ||\
bail "Failed to get triggers table."
run "$P4BIN -s triggers -i < $TmpFile1" \
"Loading updated table with SetDefaultDepotSpecMapField.py removed."
if [[ "$CMDEXITCODE" -ne 0 ]]; then
errmsg "Failed to udpate triggers table."
ErrorCount+=1
OverallReturnStatus=1
fi
else
msg "Triggers table update not required, SetDefaultDepotSpecMapField.py not found."
fi
msg "${H2}\\nUpdating depot spec Map fields."
# Failures in this next block updating depot spec Map fields, will be
# reported but will not cause an abort. Manual post-processing would be
# required if there are failures here.
for data in $("$P4BIN" -ztag -F "%name%:%type%" depots); do
vvmsg "d=[$data]"
[[ "$data" == *":"* ]] || bail "Unexpected output. Bailling."
Depot="${data%%:*}"
DepotType="${data##*:}"
case "$DepotType" in
(archive|remote) continue ;;
(stream|local|spec|unload) true;;
(*)
warnmsg "Processing depot $Depot of UKNOWN type $DepotType."
WarningCount+=1
;;
esac
ExpectedMapFieldValue="/p4/$SDPInstance/depots/$Depot/..."
DefaultMapFieldValue="$Depot/..."
ActualMapFieldValue=$("$P4BIN" -ztag -F "%Map%" depot -o "$Depot")
if [[ "$ActualMapFieldValue" == "$DefaultMapFieldValue" ]]; then
msg "Skipping update of depot $Depot; it is already correct."
else
# Get the current depot spec, sans the 'Map:' field value.
"$P4BIN" depot -o "$Depot" | grep -v '^#' | grep -v '^Map:' \
> "$TmpPrefix.$Depot.p4s"
# Append the correct Map field value to the end of the spec file.
echo "Map: $DefaultMapFieldValue" >> "$TmpPrefix.$Depot.p4s"
# Load the updated spec.
run "$P4BIN -s depot -i < $TmpPrefix.$Depot.p4s" \
"Updating depot spec for depot $Depot."
if [[ "$CMDEXITCODE" -ne 0 ]]; then
errmsg "Failed to load this updated depot spec:$(cat "$TmpPrefix.$Depot.p4s")"
ErrorCount+=1
OverallReturnStatus=1
fi
fi
done
# Be tidy
for f in "$TmpFile1" "$TmpFile2" "$TmpPrefix"*; do GARBAGE+="$f"; done
if [[ "$OverallReturnStatus" -eq 0 ]]; then
msg "${H}\\nAll processing completed successfully.\\n"
else
msg "${H}\\nProcessing completed, but with errors. Scan above output carefully.\\n"
fi
# Illustrate using $SECONDS to display runtime of a script.
msg "That took $((SECONDS/3600)) hours $((SECONDS%3600/60)) minutes $((SECONDS%60)) seconds.\\n"
# See the terminate() function, which is really where this script exits.
exit "$OverallReturnStatus"