130 lines
4.1 KiB
Bash
130 lines
4.1 KiB
Bash
|
|
# Library of utility functions for manipulating/controlling bash-internals
|
|
# Not intended to be executed directly
|
|
|
|
source $(dirname $(realpath "${BASH_SOURCE[0]}"))/console_output.sh
|
|
|
|
copy_function() {
|
|
local src="$1"
|
|
local dst="$2"
|
|
[[ -n "$src" ]] || \
|
|
die "Expecting source function name to be passed as the first argument"
|
|
[[ -n "$dst" ]] || \
|
|
die "Expecting destination function name to be passed as the second argument"
|
|
src_def=$(declare -f "$src") || [[ -n "$src_def" ]] || \
|
|
die "Unable to find source function named ${src}()"
|
|
dbg "Copying function ${src}() to ${dst}()"
|
|
# First match of $src replaced by $dst
|
|
eval "${src_def/$src/$dst}"
|
|
}
|
|
|
|
rename_function() {
|
|
local from="$1"
|
|
local to="$2"
|
|
[[ -n "$from" ]] || \
|
|
die "Expecting current function name to be passed as the first argument"
|
|
[[ -n "$to" ]] || \
|
|
die "Expecting desired function name to be passed as the second argument"
|
|
dbg "Copying function ${from}() to ${to}() before unlinking ${from}()"
|
|
copy_function "$from" "$to"
|
|
dbg "Undefining function $from"
|
|
unset -f "$from"
|
|
}
|
|
|
|
# Return 0 if the first argument matches any subsequent argument exactly
|
|
# otherwise return 1.
|
|
contains() {
|
|
local needle="$1"
|
|
local hay # one piece of the stack at a time
|
|
shift
|
|
#dbg "Looking for '$1' in '$@'"
|
|
for hay; do [[ "$hay" == "$needle" ]] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
not_contains(){
|
|
if contains "$@"; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Retry a command on a particular exit code, up to a max number of attempts,
|
|
# with exponential backoff.
|
|
#
|
|
# Usage: err_retry <attempts> <sleep ms> <exit_code> <command> <args>
|
|
# Where:
|
|
# attempts: The number of attempts to make.
|
|
# sleep ms: Number of milliseconds to sleep (doubles every attempt)
|
|
# exit_code: Space separated list of exit codes to retry. If empty
|
|
# then any non-zero code will be considered for retry.
|
|
#
|
|
# When the number of attempts is exhausted, exit code is 126 is returned.
|
|
#
|
|
# N/B: Make sure the exit_code argument is properly quoted!
|
|
#
|
|
# Based on work by 'Ayla Ounce <reacocard@gmail.com>' available at:
|
|
# https://gist.github.com/reacocard/28611bfaa2395072119464521d48729a
|
|
err_retry() {
|
|
local rc=0
|
|
local attempt=0
|
|
local attempts="$1"
|
|
local sleep_ms="$2"
|
|
local -a exit_codes
|
|
((attempts>1)) || \
|
|
die "It's nonsense to retry a command less than twice, or '$attempts'"
|
|
((sleep_ms>0)) || \
|
|
die "Refusing idiotic sleep interval of $sleep_ms"
|
|
local zzzs
|
|
zzzs=$(awk -e '{printf "%f", $1 / 1000}'<<<"$sleep_ms")
|
|
local nzexit=0 #false
|
|
local dbgspec
|
|
if [[ -z "$3" ]]; then
|
|
nzexit=1; # true
|
|
dbgspec="non-zero"
|
|
else
|
|
exit_codes=("$3")
|
|
dbgspec="[${exit_codes[*]}]"
|
|
fi
|
|
|
|
shift 3
|
|
|
|
dbg "Will retry $attempts times, sleeping up to $zzzs*2^$attempts or exit code(s) $dbgspec."
|
|
local print_once
|
|
print_once=$(echo -n " + "; printf '%q ' "${@}")
|
|
for attempt in $(seq 1 $attempts); do
|
|
# Make each attempt easy to distinguish
|
|
if ((nzexit)); then
|
|
msg "Attempt $attempt of $attempts (retry on non-zero exit):"
|
|
else
|
|
msg "Attempt $attempt of $attempts (retry on exit ${exit_codes[*]}):"
|
|
fi
|
|
if [[ -n "$print_once" ]]; then
|
|
msg "$print_once"
|
|
print_once=""
|
|
fi
|
|
"$@" && rc=$? || rc=$? # work with set -e or +e
|
|
msg "exit($rc)" |& indent 1 # Make easy to debug
|
|
|
|
if ((nzexit)) && ((rc==0)); then
|
|
dbg "Success! $rc==0" |& indent 1
|
|
return 0
|
|
elif ((nzexit==0)) && not_contains $rc "${exit_codes[@]}"; then
|
|
dbg "Success! ($rc not in [${exit_codes[*]}])" |& indent 1
|
|
return $rc
|
|
elif ((attempt<attempts)) # No sleep on last failure
|
|
then
|
|
msg "Failure! Sleeping $zzzs seconds" |& indent 1
|
|
sleep "$zzzs"
|
|
fi
|
|
zzzs=$(awk -e '{printf "%f", $1 + $1}'<<<"$zzzs")
|
|
done
|
|
msg "Retry attempts exhausted"
|
|
if ((nzexit)); then
|
|
return $rc
|
|
else
|
|
return 126
|
|
fi
|
|
}
|