#!/bin/bash
set -eu
# set -x

### --help Info: rsync files, custom/modules и pg_backup для Eva HA с master на replica.
### --help Info:
### --help Info: По-умолчанию выполнит частичный rsync по макерам RDisk.
### --help Info: SPEC https://bcrm.carbonsoft.ru/project/Document/DOC-013742#spec-master-slejv
### --help Info:
### --help Usage: rsync_companion.sh [--full] [--bwlimit=50000] [--clean]
### --help Usage:
### --help Usage:    --full - выполнить полный rsync файлов
### --help Usage:
### --help Usage:    --clean - очистить fail маркер
### --help Usage:
### --help Usage:    --bwlimit - ограничение скорости, по-умолчанию 50Mbit/s
### --help Usage:
### --help Example: rsync_companion.sh
### --help Example: rsync_companion.sh --full
### --help Example: rsync_companion.sh --clean
### --help Example: rsync_companion.sh --bwlimit=50000 --full
### --help Example:
__SILENT=TRUE
. /opt/fox_utils/crab_sys.sh
[[ $@ =~ --help ]] && sys::usage "$@"
sys::arg_parse "$@"


_echo(){
	local f
	# Вызов foo.sh -> source common_auth.sh -> source common.sh -> main() -> ca::echo() -> _echo()
	# в FUNCNAME будет "_echo ca::echo main main"
	# Вызов script2.sh -> source common.sh -> main() -> somefunc() -> _echo
	# в FUNCNAME будет "_echo somefunc main main"
	# Пропускаем все "echo" функции.
	for f in ${FUNCNAME[@]}; do
		[[ $f == source ]] && continue
		[[ $f =~ echo ]] && continue
		break
	done
	echo -e "$(date --rfc-3339=seconds) - $(basename $0) - $f - $@"
	return 0
}

_echo "START $0"

. /opt/CONFIG
declare RET
declare HA_ROLE
declare HA_COMPANION_IP
declare HA_FILE_MARKERS
declare PROGRESS_FOLDER
declare PATTERNS_FILE
declare FILES_DIR
declare PG_BCP_DIR
declare MODULES_DIR
declare FAIL_MARKER
declare BWLIMIT
declare RSYNC_PARAMS


#######################################
# Выполнит rsync кастомных модулей.
# Модулей может не быть, тогда ничего не делает.
#######################################
full_rsync_modules(){
	local ret
	if [[ ! -d $MODULES_DIR ]]; then
		_echo "Нет кастомных модулей, пропускаем"
		return 0
	fi
	_echo "Синхронизация кастомных модулей"
	ret=1
	for cnt in 1 2 3; do
		rsync ${RSYNC_PARAMS[@]}  --delete \
			"$MODULES_DIR/" \
			"$HA_COMPANION_IP:$MODULES_DIR/" && ret=0 || ret=$?
		((cnt >= 2 && ret == 0)) && break
	done
	if ((ret != 0)); then
		return $ret
	fi
	return 0
}


#######################################
# Частичный rsync резервных копий postgres.
#######################################
full_rsync_files(){
	local ret args
	args=()
	[[ -z ${ARG_NO_DELETE:-} ]] && args+=(--delete)
	# - формирует строку rsync и запускает rsync дважды
	ret=1
	_echo "Полная синхронизация файлов"
	for cnt in 1 2 3; do
		rsync ${RSYNC_PARAMS[@]} ${args[@]} \
			"$FILES_DIR/" \
			"$HA_COMPANION_IP:$FILES_DIR/" && ret=0 || ret=$?
		((cnt >= 2 && ret == 0)) && break
	done
	if ((ret != 0)); then
		return $ret
	fi
	return 0
}


#######################################
# Полный rsync файлов.
# Должно запускаться раз в сутки через deferred_job.
#######################################
full_rsync_pg_backup(){
	local ret args
	args=()
	[[ -z ${ARG_NO_DELETE:-} ]] && args+=(--delete)
	# - формирует строку rsync и запускает rsync дважды
	ret=1
	_echo "Полная синхронизация файлов"
	for cnt in 1 2 3; do
		rsync ${RSYNC_PARAMS[@]} ${args[@]} \
			"$PG_BCP_DIR/" \
			"$HA_COMPANION_IP:$PG_BCP_DIR/" && ret=0 || ret=$?
		((cnt >= 2 && ret == 0)) && break
	done
	if ((ret != 0)); then
		return $ret
	fi
	return 0
}


#######################################
# Полный rsync файлов и модулей
#######################################
full_rsync(){
	full_rsync_files
	full_rsync_modules
	full_rsync_pg_backup
	return 0
}


#######################################
# Частичный rsync файлов по маркерам RDisk.
# Должно запускаться несколько раз в част через deferred_job,
# ориентировочно, раз в 10 минут.
#######################################
partial_rsync_files(){
	local f ret cnt dst
	_echo "Частичная синхронизация файлов"
	# - мувает /tmp/rdisk_files/* в /tmp/rdisk_files/in_progress
	find "$HA_FILE_MARKERS/" -maxdepth 1 -type l -execdir mv {} "$PROGRESS_FOLDER/" \;
	>"$PATTERNS_FILE"
	for f in "$PROGRESS_FOLDER/"*; do
		[[ ! -L $f ]] && continue
		dst="$(readlink "$f")"
		if [[ ! ${dst:-} =~ $FILES_DIR ]]; then
			_echo "Исходный файл в другой директории: ! $dst =~ $FILES_DIR"
			return 2
		fi
		dst="${dst/$FILES_DIR\//}"
		echo "$dst" >> "$PATTERNS_FILE"
	done

	# - формирует строку rsync и запускает rsync дважды
	ret=1
	for cnt in 1 2 3; do
		# --delete здесь не нужен, его сделает full_rsync
		rsync ${RSYNC_PARAMS[@]} --no-recursive --files-from="$PATTERNS_FILE" \
			"$FILES_DIR/" \
			"$HA_COMPANION_IP:$FILES_DIR/" && ret=0 || ret=$?
		((cnt >= 2 && ret == 0)) && break
	done
	# - если успешно удаляет in_progress/*
	if ((ret == 0)); then
		find "$PROGRESS_FOLDER/" -maxdepth 1 -type l -delete
	else
		return $ret
	fi

	cnt="$(cat $PATTERNS_FILE)"
	cnt="$(wc -l <<< "$cnt")"
	_echo "Синхронизировали $cnt файлов и папок"
	rm -f "$PATTERNS_FILE"
	return 0
}


#######################################
# Частичный rsync кастомных модулей.
#######################################
partial_rsync_modules(){
	# Модулей немного, они небольшие, можно всегда запускать полный rsync
	full_rsync_modules
	return 0
}


#######################################
# Частичный rsync резервных копий postgres.
#######################################
partial_rsync_pg_backup(){
	# Ничего не делаем на текущем этапе, т.к. бекапы могут быть тяжёлыми,
	# и это замедлит частичный бекап файлов.
	# Это некритично, т.к. бекап БД делается раз в сутки.
	return 0
}


_main(){
	set -e
	local markers progress files
	# если нет настроено, то ничего не делаем
	# если слейв, то ничего не делаем
	[[ -z "${HA_COMPANION_IP:-}" || "${HA_ROLE:-master}" == "replica" ]] && return 0
	# Проверка на обновление
	[[ -f /mnt/shared/upgrade_running ]] && return 0

	# проверяем, что есть папка
	[[ ! -d "$PROGRESS_FOLDER" ]] && mkdir -p "$PROGRESS_FOLDER"

	# - полный rsync для /opt/eva-app/files раз в сутки
	# ?тут сравнение времени, должна быть ночь?
	if [[ ${ARG_FULL:-} == TRUE ]]; then
		full_rsync
	else
		# смотрит папку /tmp/rdisk_files/
		# - если нет файлов в  /tmp/rdisk_files/in_progress/*
		#   и /opt/fox_conf/vm/*/rootfs/mnt/tmp/tmp/rdisk_files/*
		#   то sleep и повтор
		## !!!почему sleep, если deffered_job?!!!
		## !!!если идея в том, чо это демон, то зачем deffered_job?!!!
		## !!!пока deffered_job, без слипа!!!
		markers="$(find "$HA_FILE_MARKERS/" -maxdepth 1 -type l)"
		progress="$(find "$PROGRESS_FOLDER/" -maxdepth 1 -type l)"
		while [[ -n "${markers:-}${progress:-}" ]] ; do
			partial_rsync_files
			markers="$(find "$HA_FILE_MARKERS/" -maxdepth 1 -type l)"
			progress="$(find "$PROGRESS_FOLDER/" -maxdepth 1 -type l)"
			[[ -n "${markers:-}${progress:-}" ]] && ret=0 || ret=$?
			sleep 1
		done
		partial_rsync_modules
		partial_rsync_pg_backup
	fi
	return 0
}


#######################################
# main:
# - выполнит нужные проверки
# - запустит рабочую функцию
# - оставит маркер об ошибках
#######################################
main(){
	local crab_sys_trap_err
	if pidof -csxo %PPID "${0##*/}"; then
		# На этом этапе, ещё не нужно алярмить
		_echo "Already running" >&2
		return 1
	fi

	if [[ -z ${HA_ROLE:-} || -z ${HA_COMPANION_IP:-} || -z ${HA_FILE_MARKERS:-} ]]; then
		return 0
	fi

	BWLIMIT="${ARG_BWLIMIT:-50000}"
	RSYNC_PARAMS=(-avhP -i --progress --numeric-ids --bwlimit="$BWLIMIT")

	FAIL_MARKER="/mnt/shared/files/rsync_companion_fail"
	PROGRESS_FOLDER="$HA_FILE_MARKERS/in_progress"
	PATTERNS_FILE="$PROGRESS_FOLDER/patterns.list.$$"
	FILES_DIR="/opt/eva-app/files"
	PG_BCP_DIR="/opt/eva-app/backup"
	MODULES_DIR="/opt/eva-app/custom/modules"

	if [[ ${ARG_CLEAN:-} ]]; then
		[[ $FAIL_MARKER ]] && rm -f "$FAIL_MARKER"
		_echo "Маркер удалён"
		return 0
	elif [[ -f $FAIL_MARKER ]]; then
		_echo "Прошлый rsync завершился неудачно."
		# Завершаться не нужно, возможно проблему исправили
		# return 4
	fi

	## Нужно оставить маркер, в случае ошибки.
	## Проблема в том, что подключен crab_sys, и его trap ERR всегда упадёт.
	## Поэтому, временно сохраняется значение trap ERR и снимается errexit,
	## сабшелу включается errexit, чтобы он упал при случае.
	## В конце, retcode сабшела обрабатывается и создаётся маркер для оркестратора.
	# Сохранение crab_sys trap ERR
	crab_sys_trap_err="$(trap -p ERR)"
	# Для вывода дебага по ошибкам, если они произошли
	trap 'set -x; ret=$(__exit $?); set +x; echo $ret' ERR
	# Снятие errexit
	set +e
	(
		# Сабшелу, напротив, включается errexit
		set -e
		_main "$@"
	)
	RET=$?
	# Возврат общего errexit
	set -e
	# Возврат crab_sys trap ERR
	eval $crab_sys_trap_err

	if ((RET != 0)); then
		date '+%s' > "$FAIL_MARKER"
		return $RET
	elif [[ -f $FAIL_MARKER ]]; then
		[[ $FAIL_MARKER ]] && rm -f "$FAIL_MARKER"
		_echo "Маркер удалён"
	fi
	return 0
}


main "$@"

_echo "SUCCESS $0"
exit 0
