#!/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
#
# The module will backup a 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.

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

JobName="backup-bareos-fd"
#shellcheck source=../../environment.in
. ./environment
. ./database/setup_local_db.sh

# setup local database server
DBNAME="backuptest"
TESTPGHOST="${dbHost}"
PSQL="${POSTGRES_BIN_PATH}/psql --no-psqlrc --host ${TESTPGHOST}"

[ -d "${TESTPGHOST}" ] && rm -R "${TESTPGHOST}"
mkdir -p "${TESTPGHOST}"
[ $EUID -eq 0 ] && chown postgres "${TESTPGHOST}"

pushd database >/dev/null
setup_local_db "${TESTPGHOST}"

# 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;
"
popd >/dev/null

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

start_test

cat <<END_OF_DATA >${tmp}/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/log1.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} 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}/log1.out" "Full Backup not found!"

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

# We want to test with wal compression enabled
if ((${PG_VERSION} < 15)); then
  echo "PG_VERSION is ${PG_VERSION} (<15), so we set 'wal_compression=on'"
  CMP="on"
else
  # postgres 15+
  echo "PG_VERSION is ${PG_VERSION} (15+), so we set 'wal_compression=lz4'"
  CMP="lz4"
fi
${PSQL} postgres -c "alter system set wal_compression='${CMP}';"
${PSQL} postgres -c "select pg_reload_conf();"
# check compression is enabled
if [ "$(${PSQL} postgres -tA -c "select setting from pg_settings where name='wal_compression';")" != "${CMP}" ];then
  echo "compression failed to be set"
  exit 2
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}/log2.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} 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}/log2.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/log3.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
run job=${JobName} 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}/log3.out" "2nd Incremental Backup not found!"

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

# Now stop database and try a restore
pushd database/ >/dev/null
echo "destroy pg_cluster"
local_db_stop_server "$TESTPGHOST"
# Save previous log
[ -d "data/log" ] && cp -a data/log/* log/
rm -Rf data
rm -Rf wal_archive
echo "------------ stopped"
popd >/dev/null

cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/log4.out
setdebug level=150 trace=1 timestamp=1 client=bareos-fd
restore client=bareos-fd fileset=PluginTest 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}/log4.out" "Restore Backup not ok!"
if [ ${estat} -ne 0 ]; then
  exit ${estat}
fi
check_for_zombie_jobs storage=File

# Check restore objectindex order the sql should not return result
cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages
@$out ${tmp}/objectindex_check.out
sqlquery
select j1.jobid, j1.objectindex, j2.jobid, j2.objectindex from restoreobject j1, restoreobject j2 where j1.jobid < j2.jobid and j1.objectindex > j2.objectindex;
END_OF_DATA
run_bconsole
expect_grep "No results to list." "${tmp}/objectindex_check.out" "The object indices were created out of order."

sleep 1

pushd database >/dev/null
#sometimes the pid file remains
rm -f data/postmaster.pid || :
# reset log file
rm -f data/log/* || :
rm -f data/pg_wall/* || :

# 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="${current_test_directory}/database/data/recovery.conf"
else
  # postgres 12+
  echo "PG_VERSION is ${PG_VERSION} so 12+, using postgresql.conf and recovery.signal"
  recovery_file="${current_test_directory}/database/data/recovery.signal"
fi
restore_command="restore_command = 'cp ${current_test_directory}/database/wal_archive/%f %p'"
if [ ! -f "${recovery_file}" ]; then
  echo "${TestName} ${recovery_file} is missing from restore"
  exit 1
fi
if ((${PG_VERSION} < 12)); then
  echo "${restore_command}" >>"${recovery_file}"
else
  echo "${restore_command}" >>"${current_test_directory}/database/data/postgresql.conf"
fi

echo "Restart restored pg_cluster"
local_db_start_server "${TESTPGHOST}"
popd >/dev/null

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

pushd database/ >/dev/null
local_db_stop_server "${TESTPGHOST}"
popd >/dev/null

check_two_logs "${tmp}/log1.out" "${tmp}/log4.out"
if (grep -q "for INCR" ${tmp}/sql.log); then
  estat=0
else
  echo "Error: Database rows not found"
  estat=1
fi

if (grep -q "WARN" database/log/postgresql-*.log); then
  echo "Error: Database WARNING found in postgresql log"
  estat=1
fi
if (grep -q "ERR" database/log/postgresql-*.log); then
  echo "Error: Database ERRORS found in postgresql log"
  estat=1
fi

end_test
