#!/bin/bash

# BAREOS - Backup Archiving REcovery Open Sourced
#
# Copyright (C) 2025-2026 Benjamin Somers, IMT Atlantique
# Copyright (C) 2025-2026 Bareos GmbH & Co. KG
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of version three of the GNU Affero General Public
# License as published by the Free Software Foundation, which is
# listed in the file LICENSE.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

set -euo pipefail
#
# This systemtest tests the Incus FD plugin
#
TestName="$(basename "$(pwd)")"
export TestName

JobName=backup-incus-ct
#shellcheck source=../../environment.in
. ./environment

export PATH="$current_test_directory/sbin:$current_test_directory/bin:$PATH"

JobName=backup-incus-ct
#shellcheck source=../../scripts/functions
. "$BAREOS_SCRIPTS_DIR"/functions

INSTANCE="${INCUS_CT_NAME}"
NEWINSTANCE="${INSTANCE}-restored"

wait_for_network()
{
  while ! incus exec "$1" -- ping -c1 bareos.com 2>/dev/null; do
    sleep 2
  done
}

sync_fs()
{
  incus exec "$INSTANCE" sync
}

cleanup_instances()
{
  incus rm -f "$INSTANCE" 2>/dev/null || :
  incus rm -f "$NEWINSTANCE" 2>/dev/null || :
}

trap cleanup_instances EXIT

cleanup_instances
incus launch "${INCUS_CT_BASE_NAME}" "$INSTANCE"
wait_for_network "$INSTANCE"
incus exec "$INSTANCE" apk add attr
incus file push - "$INSTANCE/old-file" <<<foobar
incus exec "$INSTANCE" -- ln -s /old-file /slinked-file
incus exec "$INSTANCE" -- ln -s /etc /slinked-etc
sync_fs

start_test

cat <<END_OF_DATA >$tmp/bconcmds
@#
@# First, perform a full backup
@#
@$out $NULL_DEV
messages
@$out ${runner_tmp}/full.out
label volume=TestVolume001 storage=File pool=Full
run job=$JobName level=Full yes
status director
status client
status storage=File
wait
messages
END_OF_DATA

run_bconsole "$@"
check_log "${runner_tmp}/full.out"

incus file push - "$INSTANCE/new-file" <<<bazqux
incus exec "$INSTANCE" -- setfattr -n user.foobar -v bazqux /new-file
incus exec "$INSTANCE" -- setfattr -n user.barqux -v foobar /etc
incus exec "$INSTANCE" ln /new-file /hlinked-file
sync_fs

# This is needed to later allow overriding the device
incus config device override "$INSTANCE" root

cat <<END_OF_DATA >$tmp/bconcmds
@#
@# Then, perform an incremental backup to get the new file
@#
@$out ${runner_tmp}/incr.out
label volume=TestVolume002 storage=File pool=Incremental
run job=$JobName level=Incremental yes
status director
status client
status storage=File
wait
messages
END_OF_DATA

run_bconsole "$@"
check_log "${runner_tmp}/incr.out"

cat <<END_OF_DATA >$tmp/bconcmds
@$out ${runner_tmp}/incr-list.out
list files jobid=$(last_jobid_or_zero)
END_OF_DATA
run_bconsole "$@"

files="$(grep "^ @INCUS/instances/$INSTANCE/container/rootfs/" "${runner_tmp}/incr-list.out" | grep -v "^ @INCUS/instances/$INSTANCE/container/rootfs/var/log/messages" | sort)"
actualfiles=(etc hlinked-file new-file)
expected="$(printf " @INCUS/instances/$INSTANCE/container/rootfs/%s\n" "${actualfiles[@]}")"
if [ "$files" != "$expected" ]; then
  echo "Unexpected list of modified files. Expected:"
  echo "$expected"
  echo "Got:"
  echo "$files"
  exit 1
fi

incus rm -f "$INSTANCE"

cat <<END_OF_DATA >$tmp/bconcmds
@#
@# Now, restore the incremental backup to a new instance
@#
@$out ${runner_tmp}/restore.out
wait
restore client=bareos-fd fileset=IncusTestCT select all done pluginoptions=python:restore_instance=$NEWINSTANCE:restore_config_override=user.foo=bar:restore_config_override=user.baz=qux:restore_device_override=root,user.foo=bar
yes
wait
messages
END_OF_DATA

run_bconsole "$@"
check_log "${runner_tmp}/restore.out"

incus start "$NEWINSTANCE"

if [ "$(incus file pull "$NEWINSTANCE/old-file" -)" = "foobar" ] \
  && [ "$(incus file pull "$NEWINSTANCE/new-file" -)" = "bazqux" ]; then
  echo "Restored file contents OK"
else
  echo "Restored files ERROR: files have changed"
  dstat=1
  exit 1
fi

# We make extra sure that we didn't accidentally change file types
if incus exec "$NEWINSTANCE" -- test -h /slinked-file \
  && incus exec "$NEWINSTANCE" -- test -h /slinked-etc \
  && incus exec "$NEWINSTANCE" -- test -d /etc; then
  echo "Files kept their types"
else
  echo "Files not restored properly"
  dstat=1
  exit 1
fi

# We check extended attributes
if [ "$(incus exec "$NEWINSTANCE" -- getfattr --only-values -n user.foobar /new-file)" = "bazqux" ] \
  && [ "$(incus exec "$NEWINSTANCE" -- getfattr --only-values -n user.barqux /etc)" = "foobar" ]; then
  echo "Extended attributes preserved"
else
  echo "Extended attributes lost"
  dstat=1
  exit 1
fi

# We check that hard links work
if (("$(incus exec "$NEWINSTANCE" -- stat -c%h /new-file)" == 2)); then
  echo "Hard links preserved"
else
  echo "Hard links broken"
  dstat=1
  exit 1
fi

# We check that configuration overrides work
if [ "$(incus config get "$NEWINSTANCE" user.foo)" = "bar" ] \
  && [ "$(incus config get "$NEWINSTANCE" user.baz)" = "qux" ] \
  && [ "$(incus config device get "$NEWINSTANCE" root user.foo)" = "bar" ]; then
  echo "Configuration override applied"
else
  echo "Configuration override not applied"
  dstat=1
  exit 1
fi

incus rm -f "$NEWINSTANCE"

check_for_zombie_jobs storage=File

end_test
