#!/bin/bash
set -e
set -o pipefail
set -u
# This systemtest tests the plugin functionality
# of the Bareos FD by using the supplied module
# bareos-fd-postgresql with debian native pg_cluster tools
#
# The module will backup a debian PostgreSQL cluster.
# Full, Incremental after changes, Incremental after no changes
# Stop the PostgreSQL, destroy the data directory
# Restore the server data directory and wal archives
# Restart and check the PostgreSQL cluster.
#
# It can be used for any PostgreSQL version >= 10.
# This test needs to be run as root, as it use system pg_*cluster
# commands.

TestName="$(basename "$(pwd)")"
export TestName

JobName="backup-bareos-fd"
#shellcheck source=../environment.in
. ./environment
. /etc/os-release
if ([ -z "${ID}" ] || [ "${ID}" != "debian" ] && [ "${ID}" != "ubuntu" ]);then
 echo "${TestName} test skipped: not debian/ubuntu"
 exit 77;
fi
if [ $UID -ne 0 ]; then
# skip test
 echo "${TestName} test skipped: needs to be root to use pg_*cluster system tools"
 exit 77;
fi

# We need the server version that will be used
PG_VERSION=$(pg_lsclusters --no-header | awk -F " " '{print $1}' | sort -n | head -n 1)
if [ ${PG_VERSION} -lt 10 ]; then
# skip test
 echo "${TestName} test skipped: not compatible with PG <= 10"
 exit 77;
fi
if [ $(pg_lsclusters ${PG_VERSION} regress | grep "regress" -c) -eq 1 ];then
    pg_dropcluster ${PG_VERSION} regress --stop
    rm -rf /var/tmp/postgresql/wal_archives
fi

pg_createcluster ${PG_VERSION} regress
mkdir -p /var/tmp/postgresql/wal_archives
chown postgres:postgres /var/tmp/postgresql/wal_archives
# Adjust configuration
{
  echo "wal_level = archive"
  echo "archive_mode = on"
  echo "archive_command = 'cp %p /var/tmp/postgresql/wal_archives/%f'"
  echo "max_wal_senders = 10"
  echo "log_connections = on"
  echo "log_disconnections = on"
  echo "log_min_messages = info"
  echo "log_min_error_statement = info"
  echo "log_error_verbosity = verbose"
  echo "log_statement = 'all'"
  echo "log_temp_files = 0"
} >> /etc/postgresql/${PG_VERSION}/regress/conf.d/regress.conf
pg_ctlcluster ${PG_VERSION} regress start

# setup local database server
DBNAME="backuptest"
TESTPGHOST="/var/run/postgresql/"
TESTPGPORT=5433
PSQL="psql --host ${TESTPGHOST} --port ${TESTPGPORT} "
su postgres -c "${PSQL} -d postgres -c 'CREATE ROLE root WITH SUPERUSER CREATEDB CREATEROLE REPLICATION LOGIN'"
# Create Test DB with table and 1 statement
${PSQL} postgres -c "create database ${DBNAME}"
${PSQL} ${DBNAME} -c "
create table t(id serial primary key, text varchar(20), created_on timestamp);
insert into t (text, created_on) values ('test for FULL backup', current_timestamp);
select * from t;
"

#shellcheck source=../scripts/functions
. "${rscripts}"/functions

start_test

cat <<END_OF_DATA >${tmp}/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/dlog1.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} fileset=PluginTestDebian yes
wait
setdebug level=0 client=bareos-fd
status director
status client
status storage=File
wait
messages
END_OF_DATA

# Create activity on the cluster during the backup
${PSQL} ${DBNAME} -f "database/make_cluster_activity.sql" --output ${tmp}/cluster_activity.log &

echo "First full backup stage"
run_bconsole
expect_grep "Backup OK" "${tmp}/dlog1.out" "Full Backup not found!"

if [ ${estat} -ne 0 ]; then
    exit ${estat}
fi
# Now add data to the database and run an incremental job
${PSQL} ${DBNAME} -c "insert into t (text, created_on) values ('test for INCR backup', current_timestamp)"

cat <<END_OF_DATA >${tmp}/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/dlog2.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} fileset=PluginTestDebian Level=Incremental yes
wait
setdebug level=0 client=bareos-fd
messages
END_OF_DATA

echo "First incremental backup stage"
run_bconsole
expect_grep "Backup OK" "${tmp}/dlog2.out" "First Incremental Backup not found!"

if [ ${estat} -ne 0 ]; then
    exit ${estat}
fi

# run another Incr without db changes - should result in empty backup job (only restore object)
cat <<END_OF_DATA >${tmp}/bconcmds
@$out ${NULL_DEV}
messages
@$out $tmp/dlog3.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} fileset=PluginTestDebian Level=Incremental yes
wait
setdebug level=0 client=bareos-fd
messages
END_OF_DATA
echo "Second incremental backup stage"
run_bconsole
expect_grep "Backup OK" "${tmp}/dlog3.out" "2nd Incremental Backup not found!"

if [ ${estat} -ne 0 ]; then
    exit ${estat}
fi

# Now stop database and try a restore
echo "destroy pg_cluster"
pg_dropcluster ${PG_VERSION} regress --stop
rm -rf /var/tmp/postgresql/wal_archives/*

cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/dlog4.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
restore client=bareos-fd fileset=PluginTestDebian where=/ select all done yes
wait
setdebug level=0 client=bareos-fd
messages
END_OF_DATA
echo "Restore stage"
run_bconsole

expect_grep "Restore OK" "${tmp}/dlog4.out" "Restore Backup not ok!"
if [ ${estat} -ne 0 ]; then
    exit ${estat}
fi
check_for_zombie_jobs storage=File

sleep 1

check_two_logs "${tmp}/dlog1.out" "${tmp}/dlog4.out"

# use either recovery.conf or recovery.signal
# Those files should exist after restore, the plugin create them
# postgres 11 and lower
if (( ${PG_VERSION} < 12 )); then
  echo "PG_VERSION is ${PG_VERSION} so lower than 12, using recovery.conf"
  recovery_file="/var/lib/postgresql/${PG_VERSION}/regress/recovery.conf"
else
  # postgres 12+
  echo "PG_VERSION is ${PG_VERSION} so 12+, using postgresql.conf and recovery.signal"
  recovery_file="/var/lib/postgresql/${PG_VERSION}/regress/recovery.signal"
fi
if [ ! -f "${recovery_file}" ];then
 echo "${TestName} ${recovery_file} is missing from restore"
 exit 1;
fi
restore_command="restore_command = 'cp /var/tmp/postgresql/wal_archives/%f %p'"
if (( ${PG_VERSION} < 12 )); then
  echo "${restore_command}" >> "${recovery_file}"
else
  echo "${restore_command}" >> "/etc/postgresql/${PG_VERSION}/regress/conf.d/restore.conf"
fi

pg_lsclusters ${PG_VERSION} regress

echo ""
echo "Restart restored pg_cluster"

if ! pg_ctlcluster ${PG_VERSION} regress start;then
  echo "cluster restart failed after restore"
   estat=1
fi

i=0
until ${PSQL} ${DBNAME} -c "select * from t" | grep "for INCR"  > ${tmp}/sql.log  ; do
  echo "waiting for query to succeed"
  sleep 1
  i=$((i+1))
  if [ $i -gt 10 ]; then echo "timeout waiting for query after recovery"; exit 1; fi
done
if (grep -q "for INCR" ${tmp}/sql.log)
then
   estat=0
else
   echo "Error: Database rows not found"
   estat=1
fi

if [ $(pg_lsclusters ${PG_VERSION} regress | grep "regress" -c) -eq 1 ];then
    echo "cluster recovery completed"
    pg_dropcluster ${PG_VERSION} regress --stop
    rm -rf /var/tmp/postgresql/wal_archives
fi


end_test
