807 lines
19 KiB
Bash
Executable File
807 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
VERSION=1.3.0
|
|
|
|
USERNAME=$(whoami)
|
|
CANON_REPO=https://github.com/starkandwayne/hangar
|
|
HANGAR_DIR="${HOME}/.hangar"
|
|
FLY_CMD="${HANGAR_DIR}/fly"
|
|
|
|
# Options
|
|
opt_target=${CONCOURSE_TARGET}
|
|
opt_pause_after_restore='a'
|
|
opt_backup_dir="${HANGAR_DIR}/backups/(target)"
|
|
opt_clean=""
|
|
target_pipelines=()
|
|
|
|
####################################################
|
|
# pipelines and backups info
|
|
|
|
declare -a pipelines pipeline_states
|
|
|
|
get_pipelines() {
|
|
local pipeline_info
|
|
local name
|
|
local state
|
|
|
|
pipeline_info="$(${FLY_CMD} -t $opt_target pipelines)"
|
|
if [[ "$?" -ne 0 ]]
|
|
then
|
|
fatal \
|
|
"Could not retrieve pipelines from $opt_target:" \
|
|
$pipeline_info
|
|
exit 1
|
|
fi
|
|
|
|
pipelines=()
|
|
pipeline_states=()
|
|
while read name state
|
|
do
|
|
pipelines+=("$name")
|
|
pipeline_states+=("$state")
|
|
done < <(echo "$pipeline_info" | tail -n +1)
|
|
}
|
|
|
|
declare -a backups backup_states
|
|
|
|
get_backups() {
|
|
local backup_files
|
|
local name
|
|
local state
|
|
|
|
echo "Using backup directory '$backup_dir'"
|
|
echo
|
|
|
|
shopt -s nullglob
|
|
backup_files=(${backup_dir}/*.yml)
|
|
shopt -u nullglob
|
|
backups=()
|
|
backup_states=()
|
|
for file in "${backup_files[@]}"
|
|
do
|
|
name="$( echo "$file" | sed -E 's#.*/([^/]*)\.yml$#\1#' )"
|
|
if [[ -f "${backup_dir}/${name}.state" ]]
|
|
then
|
|
state="$(cat ${backup_dir}/${name}.state)"
|
|
else
|
|
state="unknown"
|
|
fi
|
|
|
|
backups+=("$name")
|
|
backup_states+=("$state")
|
|
done
|
|
}
|
|
|
|
pipeline_state() {
|
|
name=$1
|
|
index="$(echo ${pipelines[@]/${name}//} | cut -d/ -f1 | wc -w | tr -d ' ')"
|
|
echo "${pipeline_states[$index]}"
|
|
}
|
|
|
|
pipeline_exists() {
|
|
name=$1
|
|
index="$(echo ${pipelines[@]/${name}//} | cut -d/ -f1 | wc -w | tr -d ' ')"
|
|
[[ $index -lt ${#pipelines[@]} ]]
|
|
}
|
|
|
|
backup_state() {
|
|
name=$1
|
|
index="$(echo ${backups[@]/${name}//} | cut -d/ -f1 | wc -w | tr -d ' ')"
|
|
echo "${backup_states[$index]}"
|
|
}
|
|
|
|
backup_exists() {
|
|
name=$1
|
|
index="$(echo ${backups[@]/${name}//} | cut -d/ -f1 | wc -w | tr -d ' ')"
|
|
[[ $index -lt ${#backups[@]} ]]
|
|
}
|
|
|
|
|
|
####################################################
|
|
# checks and validating functions
|
|
|
|
need_command() {
|
|
local cmd=${1:?need_command() - no command name given}
|
|
|
|
[[ -x "$(command -v $cmd)" ]] || fatal "${cmd} is not installed."
|
|
}
|
|
|
|
|
|
# First arg is path. Don't put the scheme://hostname:port in
|
|
# Second arg is the output path
|
|
# Third arg is basic auth params "username:password"
|
|
fly_curl() {
|
|
local fly_url fly_insecure fly_ca_cert insecure_opt output_opt basic_auth_opt
|
|
fly_url="$(flyrc ".targets.${opt_target}.api")${1}"
|
|
fly_insecure="$(flyrc ".targets.${opt_target}.insecure")"
|
|
fly_token="$(flyrc ".targets.${opt_target}.token.value")"
|
|
fly_ca_cert="$(flyrc ".targets.${opt_target}.ca_cert")"
|
|
[[ "$fly_insecure" == 'true' ]] && insecure_opt='-k'
|
|
[[ -n $2 ]] && output_opt="-o ${2}"
|
|
[[ -n $3 ]] && basic_auth_opt="--basic --user \"${3}\""
|
|
if [[ -n $fly_ca_cert ]] ; then
|
|
curl -sS ${insecure_opt} ${output_opt} ${basic_auth_opt} -H "Authorization: Bearer $fly_token" --cacert <(cat <<<"${fly_ca_cert}") "${fly_url}"
|
|
rc=$?
|
|
return $rc
|
|
else
|
|
curl -sS ${insecure_opt} ${output_opt} ${basic_auth_opt} -H "Authorization: Bearer $fly_token" "${fly_url}"
|
|
rc=$?
|
|
return $rc
|
|
fi
|
|
}
|
|
|
|
check_target() {
|
|
[[ -n "$opt_target" ]] || fatal "No target specified. Please set in \$CONCOURSE_TARGET or use the -t|--target option"
|
|
|
|
fly_curl "" >/dev/null || fatal "Cannot reach Concourse target at ${fly_url}"
|
|
}
|
|
|
|
check_backup_directory_empty() {
|
|
shopt -s nullglob
|
|
files=(${backup_dir}/*.{yml,state})
|
|
[[ "${#files[@]}" -eq 0 ]] || fatal "Backup directory (${backup_dir}) cannot contain any .yml or .state files"
|
|
}
|
|
|
|
get_backup_dir() {
|
|
if [[ "$opt_backup_dir" == *"(target)"* ]]
|
|
then
|
|
if [[ -z ${opt_target} ]]
|
|
then
|
|
fatal "No target specified, but backup directory uses (target) placeholder"
|
|
fi
|
|
local target=$(echo $opt_target | sed -e "s#/#\\/#g")
|
|
echo "${opt_backup_dir}" | sed -e "s/(target)/${target}/"
|
|
else
|
|
echo ${opt_backup_dir}
|
|
fi
|
|
}
|
|
|
|
need_dirs() {
|
|
backup_dir="$(get_backup_dir)" || exit 1
|
|
[[ -d "$HANGAR_DIR" ]] || mkdir -p "${HANGAR_DIR}"
|
|
[[ -n "$backup_dir" ]] || fatal "No backup directory specified"
|
|
mkdir -p -- "$backup_dir"
|
|
}
|
|
|
|
need_fly() {
|
|
|
|
echo "Ensuring ${FLY_CMD} to the matching version for the target"
|
|
|
|
local insecure_opt=''; [[ $fly_insecure == 'true' ]] && insecure_opt='-k'
|
|
local platform=$(uname -s | tr A-Z a-z)
|
|
fly_curl "/api/v1/cli?arch=amd64&platform=${platform}" "${FLY_CMD}"
|
|
|
|
if [[ $? -eq 0 && ! "$(file "${FLY_CMD}")" =~ ASCII\ text ]]; then
|
|
echo "Retrieved!"
|
|
echo ""
|
|
chmod u+x ${FLY_CMD}
|
|
else
|
|
echo "Could not fetch fly -- need to log in with HTTP Basic Auth"
|
|
read -p "username: " username
|
|
read -p "password: " -s password
|
|
auth="$username:$password"
|
|
fly_curl "/api/v1/cli?arch=amd64&platform=${platform}" "${FLY_CMD}" "${auth}"
|
|
if [[ "$(file "${FLY_CMD}")" =~ ASCII\ text ]]
|
|
then
|
|
rm ${FLY_CMD}
|
|
fatal "Could not log into Concourse to fetch fly!"
|
|
fi
|
|
chmod u+x ${FLY_CMD}
|
|
echo ""
|
|
echo "Successfully fetched"
|
|
fi
|
|
|
|
test="$(${FLY_CMD} -t ${opt_target} workers 2>&1)"
|
|
if [[ "$test" =~ not\ authorized ]]
|
|
then
|
|
echo "Log into Concourse"
|
|
${FLY_CMD} -t $opt_target login $insecure_opt
|
|
fi
|
|
|
|
[[ -x ${FLY_CMD} ]] || fatal "ERROR: ${FLY_CMD} not found or not executable -- cannot continue."
|
|
}
|
|
|
|
|
|
####################################################
|
|
# parser functions
|
|
|
|
parse_opts() {
|
|
local arg
|
|
while (( $# )); do
|
|
arg=$1 ; shift
|
|
case ${arg} in
|
|
(-t|--target)
|
|
opt_target="$1"
|
|
shift
|
|
;;
|
|
(-p|--path)
|
|
opt_backup_dir="$1"
|
|
shift
|
|
;;
|
|
(*)
|
|
if [[ $INLINE_PARSING == 'true' ]]
|
|
then
|
|
NEW_ARGS=("$arg" "$@")
|
|
return
|
|
fi
|
|
cmd_opt_parser="${action}_opt_parser"
|
|
offset=0
|
|
if command -V $cmd_opt_parser | grep "is a function" 2>/dev/null 1>&2
|
|
then
|
|
$cmd_opt_parser $arg $*
|
|
fi
|
|
[[ "$offset" -gt 0 ]] || fatal "Unknown option '$arg'"
|
|
while (( offset = offset - 1 ))
|
|
do
|
|
shift
|
|
done
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
cmd_backup_opt_parser() {
|
|
local arg
|
|
arg=$1 ; shift
|
|
case ${arg} in
|
|
(-P|--pause)
|
|
opt_pause_first=true
|
|
offset=1
|
|
;;
|
|
(-c|--clean)
|
|
opt_clean=true
|
|
offset=1
|
|
;;
|
|
(-*)
|
|
offset=0
|
|
;;
|
|
(*)
|
|
target_pipelines+=($arg)
|
|
offset=1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
cmd_restore_opt_parser() {
|
|
local arg
|
|
local opt
|
|
arg=$1 ; shift
|
|
case ${arg} in
|
|
(-P|--pause)
|
|
opt=$1
|
|
case ${opt} in
|
|
(y|yes|n|no|a|auto)
|
|
opt_pause_after_restore="${opt:0:1}"
|
|
;;
|
|
(*)
|
|
fatal "Error: $arg expects (y)es, (n)o or (a)uto"
|
|
;;
|
|
esac
|
|
offset=2
|
|
;;
|
|
(-c|--clean)
|
|
opt_clean=true
|
|
offset=1
|
|
;;
|
|
(--missing)
|
|
opt_restore_missing=true
|
|
offset=1
|
|
;;
|
|
(-*)
|
|
offset=0
|
|
;;
|
|
(*)
|
|
target_pipelines+=($arg)
|
|
offset=1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
####################################################
|
|
# common functions used by other parts of hangar
|
|
|
|
fatal() {
|
|
for msg in "$@"
|
|
do
|
|
echo >&2 $msg
|
|
done
|
|
exit 1
|
|
}
|
|
|
|
flyrc() {
|
|
spruce json "$HOME/.flyrc" | jq -Mrj "$1"
|
|
}
|
|
|
|
parse_yaml() {
|
|
local prefix=$2
|
|
local s='[[:space:]]*' w='[-a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
|
|
sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
|
|
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
|
|
awk -F$fs '{
|
|
indent = length($1)/2;
|
|
vname[indent] = $2;
|
|
for (i in vname) {if (i > indent) {delete vname[i]}}
|
|
if (length($3) > 0) {
|
|
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
|
|
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
|
|
}
|
|
}'
|
|
}
|
|
|
|
|
|
|
|
####################################################
|
|
# multi-call handlers
|
|
|
|
cmd_help() {
|
|
local topic=${1:-usage}
|
|
|
|
case ${topic} in
|
|
(usage)
|
|
cat >&2 <<EOF
|
|
USAGE: hangar <command> [arguments]
|
|
|
|
Common commands:
|
|
|
|
hangar clean Expunge the given (or default) backup directory
|
|
|
|
hangar backup Backup all pipelines
|
|
|
|
hangar restore Restore pipelines
|
|
|
|
hangar restore-state Restore the paused state of existing pipelines
|
|
|
|
hangar inventory List the backed up pipelines (inv for short)
|
|
|
|
hangar targets List the known targets, including the URL, if it's
|
|
the default target, and when last backed up to the
|
|
default backup directory.
|
|
|
|
hangar help Peruse the help! Give it a topic argument, or the name
|
|
of another command to get more detailed information.
|
|
Documentation!
|
|
|
|
Common options:
|
|
|
|
-t | --target <name> Specify the target concourse. By default, it will
|
|
use the value in \$CONCOURSE_TARGET if available.
|
|
|
|
-p | --path <path> Specify the backup path for storage or source for
|
|
restoring. By default, it is set to
|
|
~/.hangar/backups/<target>
|
|
|
|
EOF
|
|
exit 0
|
|
;;
|
|
(clean)
|
|
cat >&2 <<EOF
|
|
USAGE: hangar backup <options> <pipelines...>
|
|
|
|
Cleans out the backups from the backup directory
|
|
|
|
EOF
|
|
exit 0
|
|
;;
|
|
(backup)
|
|
cat >&2 <<EOF
|
|
USAGE: hangar backup <options> <pipelines...>
|
|
|
|
Retrieves the pipelines from concourse, and stores them in the backup directory,
|
|
including the paused state of the backup. If pipelines are specified, only those
|
|
pipelines will be backed up.
|
|
|
|
Options:
|
|
-P | --pause Pause the pipeline to be backed up before backing it up
|
|
|
|
-c | --clean Clean the backup directory before fetching the backups.
|
|
NOTE: The backup directory has to be empty before
|
|
the backups are taken.
|
|
EOF
|
|
exit 0
|
|
;;
|
|
(restore)
|
|
cat >&2 <<EOF
|
|
USAGE: hangar restore <options> <pipelines...>
|
|
|
|
Restores the pipelines from the backup directory to concourse. If pipelines are
|
|
specified, only those pipelines will be restored.
|
|
|
|
Options:
|
|
--missing Only restore pipelines that aren't present on the
|
|
target concourse.
|
|
|
|
-c | --clean Remove the backups after restoring them.
|
|
|
|
-P | --pause (yes|no) Pause or unpause the restored pipeline after restoring
|
|
it. 'yes' and 'no' can be abbreviated as 'y' and 'n'
|
|
respectively. By default, the pipelines will be
|
|
paused only if they were paused prior to being backed
|
|
up, otherwise they will be unpaused.
|
|
|
|
EOF
|
|
exit 0
|
|
;;
|
|
(*)
|
|
cat >&2 <<EOF
|
|
Unrecognized help topic '${topic}'.
|
|
Try one of these:
|
|
|
|
hangar help usage
|
|
hangar help <command>
|
|
|
|
EOF
|
|
exit 0
|
|
;;
|
|
esac
|
|
exit 1
|
|
}
|
|
|
|
cmd_clean() {
|
|
|
|
local backup
|
|
|
|
check_target
|
|
need_dirs
|
|
get_backups
|
|
|
|
local backup_dir="$(get_backup_dir)"
|
|
for backup in ${backups[@]}
|
|
do
|
|
rm "$backup_dir/$backup.yml"
|
|
rm "$backup_dir/$backup.state"
|
|
done
|
|
backups=()
|
|
}
|
|
|
|
cmd_backup() {
|
|
|
|
local pipeline
|
|
local result
|
|
|
|
check_target
|
|
need_dirs
|
|
need_fly
|
|
|
|
if [[ -n "$opt_clean" ]]
|
|
then
|
|
cmd_clean
|
|
fi
|
|
|
|
check_backup_directory_empty
|
|
|
|
get_pipelines
|
|
if [[ ${#target_pipelines[@]} -gt 0 ]]
|
|
then
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
if ! pipeline_exists $pipeline
|
|
then
|
|
fatal "$pipeline does not exist on specified Concourse target"
|
|
fi
|
|
done
|
|
else
|
|
target_pipelines=("${pipelines[@]}")
|
|
fi
|
|
|
|
echo "Backing up pipelines"
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
if [[ -n "$opt_pause_first" ]]
|
|
then
|
|
if [[ "$(pipeline_state $pipeline)" == "yes" ]]
|
|
then
|
|
echo " * ${pipeline} is already paused."
|
|
else
|
|
echo -n " * Pausing ${pipeline} first..."
|
|
result="$(${FLY_CMD} -t "$opt_target" pause-pipeline -p $pipeline)"
|
|
if [[ "$?" -ne 0 ]]
|
|
then
|
|
fatal "Could not pause pipeline ${pipeline}: $result"
|
|
fi
|
|
echo 'done!'
|
|
fi
|
|
fi
|
|
echo -n " * Fetching ${pipeline}..."
|
|
result="$(${FLY_CMD} -t "$opt_target" get-pipeline -p $pipeline)"
|
|
if [[ "$?" -ne 0 ]]
|
|
then
|
|
fatal "Could not fetch pipeline ${pipeline}: $result"
|
|
fi
|
|
echo "$result" > "${backup_dir}/${pipeline}.yml"
|
|
pipeline_state $pipeline > "${backup_dir}/${pipeline}.state"
|
|
echo 'done!'
|
|
echo ""
|
|
done
|
|
}
|
|
|
|
cmd_restore() {
|
|
|
|
local pipeline
|
|
local result
|
|
local desc
|
|
|
|
check_target
|
|
need_dirs
|
|
need_fly
|
|
|
|
echo "Restoring backups to $opt_target"
|
|
get_backups
|
|
if [[ ${#target_pipelines[@]} -gt 0 ]]
|
|
then
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
backup_exists $pipeline || fatal "No backup found for pipeline $pipeline in $backup_dir"
|
|
done
|
|
elif [[ -n "$opt_restore_missing" ]]
|
|
then
|
|
local missing_pipelines=()
|
|
get_pipelines
|
|
for pipeline in "${backups[@]}"
|
|
do
|
|
if ! pipeline_exists $pipeline
|
|
then
|
|
missing_pipelines+=($pipeline)
|
|
fi
|
|
done
|
|
target_pipelines=("${missing_pipelines[@]}")
|
|
else
|
|
target_pipelines=("${backups[@]}")
|
|
fi
|
|
|
|
if [[ "${#target_pipelines[@]}" -eq 0 ]]
|
|
then
|
|
echo "No pipelines to restore"
|
|
return
|
|
fi
|
|
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
echo -n " * Uploading ${pipeline}..."
|
|
result="$(${FLY_CMD} -t "$opt_target" set-pipeline -n -p $pipeline -c "${backup_dir}/${pipeline}.yml")"
|
|
[[ "$?" -eq 0 ]] || fatal "Could not set pipeline ${pipeline}: $result"
|
|
echo 'done!'
|
|
|
|
local pause_cmd
|
|
# TODO: why aren't we using $(backup_state $pipeline)
|
|
pipeline_state="$(cat "${backup_dir}/${pipeline}.state")"
|
|
case ${opt_pause_after_restore/a/$pipeline_state} in
|
|
(y|yes)
|
|
pause_cmd="pp"
|
|
desc="Pausing"
|
|
;;
|
|
(n|no)
|
|
pause_cmd="up"
|
|
desc="Unpausing"
|
|
;;
|
|
(*)
|
|
pause_cmd=""
|
|
;;
|
|
esac
|
|
if [[ -n "$pause_cmd" ]]
|
|
then
|
|
echo -n " * ${desc} ${pipeline}..."
|
|
result="$(${FLY_CMD} -t "$opt_target" $pause_cmd -p $pipeline)"
|
|
[[ "$?" -eq 0 ]] || fatal "$desc pipeline ${pipeline} failed: $result"
|
|
echo 'done!'
|
|
fi
|
|
|
|
if [[ -n "$opt_clean" ]]
|
|
then
|
|
echo " * Removing backup"
|
|
rm "${backup_dir}/${pipeline}.yml"
|
|
rm "${backup_dir}/${pipeline}.state"
|
|
fi
|
|
echo ""
|
|
|
|
done
|
|
}
|
|
|
|
cmd_restore_state() {
|
|
|
|
local pipeline
|
|
local result
|
|
|
|
check_target
|
|
need_dirs
|
|
need_fly
|
|
|
|
get_backups
|
|
if [[ ${#target_pipelines[@]} -gt 0 ]]
|
|
then
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
backup_exists $pipeline || fatal "No backup found for pipeline $pipeline in $backup_dir"
|
|
done
|
|
else
|
|
local present_pipelines=()
|
|
get_pipelines
|
|
for pipeline in "${backups[@]}"
|
|
do
|
|
if pipeline_exists $pipeline
|
|
then
|
|
present_pipelines+=($pipeline)
|
|
else
|
|
echo " ! WARNING: Pipeline $pipeline was not found on specified Concourse target"
|
|
fi
|
|
done
|
|
target_pipelines=("${present_pipelines[@]}")
|
|
fi
|
|
|
|
for pipeline in "${target_pipelines[@]}"
|
|
do
|
|
local pause_cmd
|
|
# TODO: why aren't we using $(backup_state $pipeline)
|
|
pipeline_state="$(cat "${backup_dir}/${pipeline}.state")"
|
|
case ${pipeline_state} in
|
|
(y|yes)
|
|
pause_cmd="pp"
|
|
state_action=" - Pausing"
|
|
;;
|
|
(n|no)
|
|
pause_cmd="up"
|
|
state_action=" - Unpausing"
|
|
;;
|
|
(*)
|
|
echo " ! WARNING: $pipeline state of ${pipeline_state} is not valid: expecting yes or no -- cannot restore"
|
|
continue
|
|
;;
|
|
esac
|
|
result="$(${HANGAR_DIR}/fly -t "$opt_target" $pause_cmd -p $pipeline)"
|
|
echo "$state_action pipeline $pipeline"
|
|
[[ "$?" -eq 0 ]] || echo " ! Could not restore state of pipeline ${pipeline}: $result"
|
|
done
|
|
}
|
|
|
|
cmd_inv() {
|
|
|
|
local pipeline
|
|
local result
|
|
|
|
check_target
|
|
need_dirs
|
|
|
|
get_backups
|
|
|
|
local on_mac=no; [[ "$(uname -s)" == "Darwin" ]] && on_mac=yes
|
|
|
|
echo "Date Paused Name"
|
|
echo "------------------ ------ --------------------------------------------------------------"
|
|
for pipeline in "${backups[@]}"
|
|
do
|
|
if [[ ${on_mac} == "yes" ]]
|
|
then
|
|
ts="$(stat -f "%Sa" -t '%Y-%m-%d@%H:%M:%S' ~/.hangar/backups/sw/vault-boshrelease.yml)"
|
|
else
|
|
ts="$(date -d "$(stat -c "%z" ${backup_dir}/${pipeline}.yml)" +"%Y-%m-%d@%H:%M:%S")"
|
|
fi
|
|
# TODO: why aren't we using $(backup_state $pipeline)
|
|
pipeline_state="$(cat "${backup_dir}/${pipeline}.state")"
|
|
printf "%-18s%-8s%s\n" "$ts" "$pipeline_state" "$pipeline"
|
|
done
|
|
}
|
|
|
|
cmd_targets() {
|
|
|
|
local backup_dir
|
|
local latest_file
|
|
local backup_date
|
|
|
|
echo Known Targets:
|
|
echo --------------
|
|
while read -r target url || [[ -n $target ]]
|
|
do
|
|
default=""
|
|
[[ "$CONCOURSE_TARGET" == "$target" ]] && default=" (default)"
|
|
echo ""
|
|
echo "$target$default"
|
|
echo " URL: $url"
|
|
opt_target="$target"
|
|
backup_dir=$(get_backup_dir)
|
|
if [[ -d $backup_dir ]]
|
|
then
|
|
latest_file=$(ls -1tr $backup_dir | tail -n1)
|
|
if [[ -f "${backup_dir}/${latest_file}" ]]
|
|
then
|
|
backup_date=$(stat -f "%Sc" -t '%Y-%m-%d@%H:%M' "${backup_dir}/${latest_file}")
|
|
echo " Last backed up to $backup_dir on $backup_date"
|
|
fi
|
|
fi
|
|
done < <(parse_yaml ~/.flyrc fly_ | grep 'fly_targets_.*_api=' | sed -E 's/^fly_targets_(.*)_api="(.*)"$/\1 \2/')
|
|
}
|
|
|
|
bad_command() {
|
|
local cmd=${1}
|
|
cat >&2 <<EOF
|
|
Unrecognized sub-command: '$cmd'
|
|
|
|
Try one of these:
|
|
|
|
hangar help
|
|
hangar version
|
|
hangar clean
|
|
hangar backup
|
|
hangar restore
|
|
hangar restore-state
|
|
hangar inv[entory]
|
|
hangar targets
|
|
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
checksum() {
|
|
if [[ -z $(command -v sha1sum) ]]; then
|
|
shasum "$@"
|
|
else
|
|
sha1sum "$@"
|
|
fi
|
|
}
|
|
|
|
####################################################
|
|
# multi-call interface
|
|
|
|
main() {
|
|
action=''
|
|
|
|
while [[ -z ${action} ]]
|
|
do
|
|
local cmd_name=${1:-help} ; shift
|
|
case "${cmd_name}" in
|
|
(ping)
|
|
exit 0
|
|
;;
|
|
(version|-v|--version)
|
|
s=$(cat ${BASH_SOURCE[0]} | checksum)
|
|
echo "hangar v${VERSION} (${s:0:12})"
|
|
exit 0
|
|
;;
|
|
(help|-h|--help|-\?)
|
|
cmd_help $*
|
|
exit 0
|
|
;;
|
|
(-t|-p)
|
|
# Common options before commmand
|
|
INLINE_PARSING=true
|
|
set -- "$cmd_name" "$@"
|
|
parse_opts "$@"
|
|
set -- "$NEW_ARGS"
|
|
INLINE_PARSING=false
|
|
;;
|
|
(clean)
|
|
# Delete all existing backups
|
|
action=cmd_clean
|
|
;;
|
|
(backup)
|
|
# Backup the pipelines
|
|
action=cmd_backup
|
|
;;
|
|
(restore)
|
|
# Restore the pipelines
|
|
action=cmd_restore
|
|
;;
|
|
(restore-state)
|
|
# Restore the paused state of existing pipelines
|
|
action=cmd_restore_state
|
|
;;
|
|
(inv|inventory)
|
|
# List the contents of the hanger
|
|
action=cmd_inv
|
|
;;
|
|
(targets)
|
|
# List available/default targets
|
|
action=cmd_targets
|
|
;;
|
|
(*)
|
|
bad_command $cmd_name
|
|
esac
|
|
done
|
|
|
|
parse_opts "$@"
|
|
$action
|
|
}
|
|
|
|
main "$@"
|
|
|
|
echo ""
|
|
# fin
|