Merge branch 'tests' into 'master'

Add a testsuite

See merge request liberate/backupninja!46
This commit is contained in:
Jerome Charaoui 2021-01-07 05:51:06 -08:00
commit 387eb85d70
21 changed files with 2473 additions and 8 deletions

29
README_tests.md Normal file
View File

@ -0,0 +1,29 @@
When developping fixes or new features for backupninja, it is highly recommended
to run the test suite to help spot potential problems.
The test suite is based on Vagrant, and is configured to rely on the VirtualBox
provider. The required package may be installed using the following command:
apt install vagrant virtualbox
On Debian 10 (buster) these packages aren't available in the default upstream
repositories, so you will need to use an alternative such as the one provided
by an individual Debian developper here:
https://people.debian.org/~lucas/virtualbox-buster/
Once the requirements are in place, the test suite may be run in this manner:
git clone git@0xacab.org:liberate/backupninja.git
cd backupninja
vagrant up
vagrant rsync
vagrant ssh -c "sudo /vagrant/test/test.sh"
It's possible to only test a specific handler with:
vagrant ssh -c "sudo /vagrant/test/test.sh rdiff"
Please report any problems with the test suite on the issue tracker at:
https://0xacab.org/liberate/backupninja/-/issues

84
Vagrantfile vendored
View File

@ -3,13 +3,81 @@
ENV["LC_ALL"] = "en_US.UTF-8"
base_box = "debian/testing64"
empty_disk = '.vagrant/tmp/empty.vdi'
lvm_disk = '.vagrant/tmp/lvm.vdi'
lukspart_disk = '.vagrant/tmp/lukspart.vdi'
luksdev_disk = '.vagrant/tmp/luksdev.vdi'
Vagrant.configure("2") do |config|
config.vm.box = "debian/stretch64"
config.vm.provision "shell", inline: <<-SHELL
locale-gen
apt-get update
apt-get install -y git automake make
git clone https://0xacab.org/liberate/backupninja.git
chown vagrant: backupninja -R
SHELL
config.vm.define "remote" do |remote|
remote.vm.box = base_box
remote.vm.hostname = "bntest1"
remote.vm.network "private_network", ip: "192.168.181.5"
remote.vm.provision "shell", inline: <<-SHELL
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
apt-get update
apt-get install -y borgbackup duplicity rdiff-backup restic rsync
sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
echo "Port 22" >> /etc/ssh/sshd_config
echo "Port 7722" >> /etc/ssh/sshd_config
systemctl reload sshd
echo -e "vagrant\nvagrant" | passwd vagrant
chown vagrant: /var/backups
SHELL
end
config.vm.define "local", primary: true do |local|
local.vm.box = base_box
local.vm.hostname = "bntest0"
local.vm.network "private_network", ip: "192.168.181.4"
local.vm.provision "shell", inline: <<-SHELL
echo "root: vagrant" >> /etc/aliases
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
apt-get update
apt-get install -y automake make dialog sshpass
cd /vagrant
./autogen.sh
./configure --prefix=/usr --mandir=/usr/share/man --sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib --libexecdir=/usr/lib
make
make install
mkdir -p /root/.ssh
yes y | ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ''
echo "StrictHostKeyChecking accept-new" >> /root/.ssh/config
echo "192.168.181.5 bntest1" >> /etc/hosts
sshpass -p vagrant scp /root/.ssh/id_ed25519.pub vagrant@bntest1:/tmp/bntest.pub
sshpass -p vagrant ssh vagrant@bntest1 "cat /tmp/bntest.pub >> /home/vagrant/.ssh/authorized_keys"
sshpass -p vagrant ssh vagrant@bntest1 "chmod 400 /home/vagrant/.ssh/authorized_keys"
ssh vagrant@bntest1 "sudo sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config"
ssh vagrant@bntest1 "sudo systemctl reload sshd"
SHELL
local.vm.synced_folder ".", "/vagrant", type: "rsync",
rsync__exclude: ".git/",
rsync__args: ["--recursive", "--delete"]
local.vm.provider :virtualbox do |vb|
unless File.exist?(empty_disk)
vb.customize ['createhd', '--filename', empty_disk, '--size', 100 ]
end
unless File.exist?(empty_disk)
vb.customize ['createhd', '--filename', lvm_disk, '--size', 100 ]
end
unless File.exist?(lukspart_disk)
vb.customize ['createhd', '--filename', lukspart_disk, '--size', 100 ]
end
unless File.exist?(luksdev_disk)
vb.customize ['createhd', '--filename', luksdev_disk, '--size', 100 ]
end
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium', empty_disk]
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 2, '--device', 0, '--type', 'hdd', '--medium', lvm_disk]
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 3, '--device', 0, '--type', 'hdd', '--medium', lukspart_disk]
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 4, '--device', 0, '--type', 'hdd', '--medium', luksdev_disk]
end
end
end

View File

@ -121,6 +121,7 @@ fi
if [ "$init" == "yes" ]; then
initstr="borg init --encryption=$encryption $execstr_repository"
debug "executing borg init"
debug "$initstr"
if [ $test = 0 ]; then
output="`su -c "$initstr" 2>&1`"
@ -168,6 +169,8 @@ fi
# include client-part and server-part
execstr="${execstr} ${excludes} $execstr_repository::$execstr_archive ${includes}"
debug "executing borg create"
debug "$nice $execstr"
if [ $test = 0 ]; then
@ -189,6 +192,7 @@ if [ "$prune" == "yes" ]; then
prune_options="${prune_options} --keep-within=${keep}"
fi
prunestr="borg prune $prune_options $execstr_repository"
debug "executing borg prune"
debug "$prunestr"
if [ $test = 0 ]; then
output="`su -c "$prunestr" 2>&1`"

View File

@ -296,6 +296,7 @@ fi
### Cleanup commands (duplicity >= 0.4.4)
# cleanup
debug "executing duplicity cleanup"
debug "$nice $execstr_precmd duplicity cleanup --force $execstr_options $execstr_serverpart"
if [ $test = 0 ]; then
export PASSPHRASE=$password
@ -315,6 +316,7 @@ fi
# remove-older-than
if [ "$keep" != "yes" ]; then
debug "executing duplicity remove-older-than"
debug "$nice $execstr_precmd duplicity remove-older-than $keep --force $execstr_options $execstr_serverpart"
if [ ! $test ]; then
export PASSPHRASE=$password
@ -337,6 +339,7 @@ fi
if [ "$keep" != "yes" ]; then
if [ "$keepincroffulls" != "all" ]; then
if version_ge "$duplicity_version" '0.6.10'; then
debug "executing duplicity remove-all-inc-of-but-n-full"
debug "$nice $execstr_precmd duplicity remove-all-inc-of-but-n-full $keepincroffulls --force $execstr_options $execstr_serverpart"
if [ $test = 0 ]; then
export PASSPHRASE=$password
@ -358,6 +361,7 @@ if [ "$keep" != "yes" ]; then
fi
### Backup command
debug "executing duplicity"
debug "$nice $execstr_precmd duplicity $execstr_command $execstr_options $execstr_source --exclude '**' / $execstr_serverpart"
if [ $test = 0 ]; then
outputfile=`maketemp backupout`

View File

@ -98,6 +98,7 @@ getconf keep 60
getconf include
getconf exclude
setsection dest
getconf directory; destdir=$directory
# strip trailing /
@ -179,6 +180,7 @@ if [ "$keep" != yes ]; then
fi
removestr="${removestr}${destdir}/${label}";
debug "executing rdiff-backup --remove-older-than"
debug "$removestr"
if [ $test = 0 ]; then
output="`su -c "$removestr" 2>&1`"
@ -251,6 +253,7 @@ set +o noglob
# include client-part and server-part
execstr="${execstr}$execstr_sourcepart $execstr_destpart"
debug "executing rdiff-backup"
debug "$nice $execstr"
if [ $test = 0 ]; then
output=`$nice su -c "$execstr" 2>&1`

177
test/backupninja.bats Normal file
View File

@ -0,0 +1,177 @@
load common
create_test_action() {
echo '#!/bin/sh' > "${BATS_TMPDIR}/backup.d/test.sh"
echo "$1 $2" >> "${BATS_TMPDIR}/backup.d/test.sh"
chmod 0750 "${BATS_TMPDIR}/backup.d/test.sh"
}
@test "general: usage information is displayed" {
run backupninja --help
[ "$status" -eq 0 ]
[ "${lines[0]}" = "/usr/sbin/backupninja usage:" ]
}
@test "general: error thrown on bad command-line option" {
run backupninja --foo
[ "$status" -eq 2 ]
[ "${lines[0]}" = "Unknown option --foo" ]
}
@test "general: logfile is created" {
run backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
[ -f "${BATS_TMPDIR}/log/backupninja.log" ]
}
@test "general: no backup actions configured is logged" {
run backupninja --test -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
[ "${lines[0]}" = "Info: No backup actions configured in '${BATS_TMPDIR}/backup.d', run ninjahelper!" ]
}
@test "general: file without suffix in action directory is ignored" {
touch "${BATS_TMPDIR}/backup.d/test"
chmod 0640 "${BATS_TMPDIR}/backup.d/test"
run backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Info: Skipping ${BATS_TMPDIR}/backup.d/test" "${BATS_TMPDIR}/log/backupninja.log"
}
@test "permissions: error thrown when backup action is owned by non-root user" {
create_test_action
chown vagrant: "${BATS_TMPDIR}/backup.d/test.sh"
run backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 2 ]
echo "${lines[0]}" | grep -qe '^Configuration files must be owned by root!'
}
@test "permissions: error thrown when backup action is world readable" {
create_test_action
chmod 0755 "${BATS_TMPDIR}/backup.d/test.sh"
run backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 2 ]
echo "${lines[0]}" | grep -qe '^Configuration files must not be world writable/readable!'
}
@test "permissions: error thrown when backup action group ownership is bad" {
create_test_action
chgrp staff "${BATS_TMPDIR}/backup.d/test.sh"
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 2 ]
echo "${lines[0]}" | grep -qe '^Configuration files must not be writable/readable by group staff!'
}
@test "reports: error report is mailed" {
create_test_action fatal test_error
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
sleep 0.1
grep -q "\*failed\* -- ${BATS_TMPDIR}/backup.d/test.sh" /var/mail/vagrant
}
@test "reports: warning report is mailed" {
create_test_action warning test_warning
setconfig backupninja.conf reportsuccess no
setconfig backupninja.conf reportwarning yes
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
sleep 0.1
grep -q "Warning: test_warning" /var/mail/vagrant
}
@test "reports: success report is mailed" {
create_test_action
setconfig backupninja.conf reportsuccess yes
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
sleep 0.1
grep -q "success -- ${BATS_TMPDIR}/backup.d/test.sh" /var/mail/vagrant
}
@test "reports: success report contains informational messages" {
create_test_action info test_info
setconfig backupninja.conf reportsuccess yes
setconfig backupninja.conf reportinfo yes
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
sleep 0.1
grep -q "Info: test_info" /var/mail/vagrant
}
@test "reports: success report contains disk space info" {
create_test_action
echo "directory = /" >> "${BATS_TMPDIR}/backup.d/test.sh"
setconfig backupninja.conf reportsuccess yes
setconfig backupninja.conf reportspace yes
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
sleep 0.1
grep -q "/dev/sda1" /var/mail/vagrant
}
@test "scheduling: runs when = 'everyday at 01' and time matches" {
create_test_action info test_info
setconfig backupninja.conf when 'everyday at 01'
run faketime -f '@2018-06-12 01:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning." "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: skips when = 'everyday at 01' and time is mismatched" {
create_test_action info test_info
setconfig backupninja.conf when 'everyday at 01'
run faketime -f '@2018-06-12 02:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Debug: skipping ${BATS_TMPDIR}/backup.d/test.sh because current time does not match everyday at 01" "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: runs when = 'Tuesday at 04' and time matches" {
create_test_action info test_info
setconfig backupninja.conf when 'Tuesday at 04'
run faketime -f '@2018-06-12 04:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning." "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: skips when = 'Tuesday at 04' and time is mismatched" {
create_test_action info test_info
setconfig backupninja.conf when 'Tuesday at 04'
run faketime -f '@2018-06-13 04:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Debug: skipping ${BATS_TMPDIR}/backup.d/test.sh because current time does not match Tuesday at 04" "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: runs when = '1st at 10' and time matches" {
create_test_action info test_info
setconfig backupninja.conf when '1st at 10'
run faketime -f '@2018-06-01 10:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning." "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: skips when = '1st at 10' and time is mismatched" {
create_test_action info test_info
setconfig backupninja.conf when '1st at 10'
run faketime -f '@2018-06-15 10:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Debug: skipping ${BATS_TMPDIR}/backup.d/test.sh because current time does not match 1st at 10" "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: runs when = '21 at 09:00' and time matches" {
create_test_action info test_info
setconfig backupninja.conf when '21 at 09:00'
run faketime -f '@2018-06-21 09:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning." "${BATS_TMPDIR}/log/backupninja.log"
}
@test "scheduling: skips when = '21 at 09:00' and time is mismatched" {
create_test_action info test_info
setconfig backupninja.conf when '21 at 09:00'
run faketime -f '@2018-06-22 09:00:00' backupninja -f "${BATS_TMPDIR}/backupninja.conf"
[ "$status" -eq 0 ]
grep -q "Debug: skipping ${BATS_TMPDIR}/backup.d/test.sh because current time does not match 21 at 09:00" "${BATS_TMPDIR}/log/backupninja.log"
}

359
test/borg.bats Normal file
View File

@ -0,0 +1,359 @@
load common
begin_borg() {
apt-get -qq install debootstrap borgbackup
if [ ! -d "$BN_SRCDIR" ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR"
fi
}
setup_borg() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.borg"
when = manual
testconnect = no
nicelevel = 0
ionicelevel =
bwlimit =
[source]
init = yes
include = ${BN_SRCDIR}
exclude = ${BN_SRCDIR}/var
create_options =
prune = yes
keep = 30d
prune_options =
cache_directory =
[dest]
user =
host = localhost
port = 22
directory = ${BN_BACKUPDIR}/testborg
archive =
compression = lz4
encryption = none
passphrase =
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.borg"
rm -rf /root/.cache/borg
}
finish_borg() {
cleanup_backups local remote
rm -rf /root/.cache/borg
}
@test "check ssh connection test" {
setconfig testconnect yes
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
greplog "Debug: Connected to ${BN_REMOTEHOST} as ${BN_REMOTEUSER} successfully$"
}
@test "check config parameter nicelevel" {
# nicelevel is 0 by default
delconfig nicelevel
testaction
greplog 'Debug: executing borg create$' '\bnice -n 0\b'
# nicelevel is defined
setconfig nicelevel -19
testaction
greplog 'Debug: executing borg create$' '\bnice -n -19\b'
}
@test "check config parameter ionicelevel" {
# no ionice by default
delconfig ionicelevel
testaction
not_greplog 'Debug: executing borg create$' '\bionice -c2\b'
# acceptable value
setconfig ionicelevel 7
testaction
greplog 'Debug: executing borg create$' '\bionice -c2 -n 7\b'
# unacceptable value
setconfig ionicelevel 10
testaction
greplog 'Fatal: The value of ionicelevel is expected to be either empty or an integer from 0 to 7. Got: 10$'
}
@test "check config parameter bwlimit" {
# no limit by default
delconfig bwlimit
testaction
not_greplog 'Debug: executing borg create$' '\s--remote-ratelimit='
# limit is defined
setconfig bwlimit 1024
testaction
greplog 'Debug: executing borg create$' '\s--remote-ratelimit=1024\b'
}
@test "check config parameter source/init" {
# do repository init by default
delconfig source init
testaction
greplog 'Debug: executing borg init$'
# do repository init
setconfig source init yes
testaction
greplog 'Debug: executing borg init$'
# don't do repository init
setconfig source init no
testaction
not_greplog 'Debug: executing borg init$'
}
@test "check config parameter source/include" {
# missing path
delconfig source include
testaction
greplog 'Fatal: No source includes specified$'
# single path
setconfig source include "$BN_SRCDIR"
testaction
greplog 'Debug: executing borg create$' "'${BN_SRCDIR}'$"
# multiple paths
setconfig_repeat source include "$BN_SRCDIR" /foo /bar
testaction
greplog 'Debug: executing borg create$' "'${BN_SRCDIR}' '/foo' '/bar'$"
}
@test "check config parameter source/exclude" {
# absent path
delconfig source exclude
testaction
not_greplog 'Debug: executing borg create$' "\s--exclude\s"
# single path
setconfig source exclude "${BN_SRCDIR}/var"
testaction
greplog 'Debug: executing borg create$' "\s--exclude '${BN_SRCDIR}/var'\s"
# multiple paths
setconfig_repeat source exclude "$BN_SRCDIR/var" "$BN_SRCDIR/foo" "$BN_SRCDIR/bar"
testaction
greplog 'Debug: executing borg create$' "\s--exclude '${BN_SRCDIR}/var' --exclude '${BN_SRCDIR}/foo' --exclude '${BN_SRCDIR}/bar'\s"
}
@test "check config parameter source/create_options" {
# absent parameter
delconfig source create_options
testaction
greplog 'Debug: executing borg create$' "\sborg create --stats --compression lz4\s\+--exclude\s"
# defined parameter
setconfig source create_options "-x --exclude-caches"
testaction
greplog 'Debug: executing borg create$' "\s\+-x --exclude-caches\s\+"
}
@test "check config parameter source/prune" {
# absent parameter, defaults enabled
delconfig source prune
testaction
greplog 'Debug: executing borg prune$'
# defined parameter, enabled
setconfig source prune yes
testaction
greplog 'Debug: executing borg prune$'
# defined parameter, disabled
setconfig source prune no
testaction
not_greplog 'Debug: executing borg prune$'
}
@test "check config parameter source/keep" {
# absent parameter, defaults to '30d'
setconfig source prune yes
delconfig source keep
testaction
greplog 'Debug: executing borg prune$' '\s--keep-within=30d\b'
# defined parameter, set to 60d
setconfig source prune yes
setconfig source keep 60d
testaction
greplog 'Debug: executing borg prune$' '\s--keep-within=60d\b'
# defined parameter, disabled, set to 0
setconfig source prune yes
setconfig source keep 0
testaction
not_greplog 'Debug: executing borg prune$' '\s--keep-within='
}
@test "check config parameter source/prune_options" {
# absent parameter
delconfig source prune_options
testaction
greplog 'Debug: executing borg prune$' "Debug: borg prune --keep-within=30d ${BN_BACKUPDIR}/testborg$"
# defined parameter
setconfig source prune_options "--keep-daily 9 --save-space"
testaction
greplog 'Debug: executing borg prune$' "\s\+--keep-daily 9 --save-space\s\+"
}
@test "check config parameter source/cache_directory" {
# absent parameter
delconfig source cache_directory
testaction
not_greplog 'Debug: export BORG_CACHE_DIR='
# defined parameter
setconfig source cache_directory "/var/cache/borg"
testaction
greplog 'Debug: export BORG_CACHE_DIR="/var/cache/borg"$'
}
@test "check config parameter dest/user" {
# absent parameter
delconfig dest user
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Fatal: Destination user not set$'
# defined parameter
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog "Debug: ssh -o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::"
}
@test "check config parameter dest/host" {
# absent parameter
delconfig dest host
setconfig dest user "$BN_REMOTEUSER"
testaction
greplog 'Fatal: Destination host not set$'
# defined parameter
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog "Debug: ssh -o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::"
}
@test "check config parameter dest/port" {
# absent parameter, defaults to 22
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
delconfig dest port
testaction
greplog "Debug: ssh -o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::"
# defined parameter
setconfig dest port 7722
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog "Debug: ssh -o PasswordAuthentication=no ${BN_REMOTEHOST} -p 7722 -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:7722${BN_BACKUPDIR}/testborg::"
}
@test "check config parameter dest/directory" {
# absent parameter
delconfig dest directory
testaction
greplog 'Fatal: Destination directory not set$'
# defined parameter
setconfig dest directory "${BN_BACKUPDIR}/testborg"
testaction
greplog 'Debug: executing borg create$' "\s${BN_BACKUPDIR}/testborg::"
}
@test "check config parameter dest/archive" {
# absent parameter, defaults to {now:%Y-%m-%dT%H:%M:%S}
delconfig dest archive
testaction
greplog 'Debug: executing borg create$' "\s${BN_BACKUPDIR}/testborg::{now:%Y-%m-%dT%H:%M:%S}"
# defined parameter
setconfig dest archive foo
testaction
greplog 'Debug: executing borg create$' "\s${BN_BACKUPDIR}/testborg::foo"
}
@test "check config parameter dest/compression" {
# absent parameter, defaults to lz4
delconfig dest compression
testaction
greplog 'Debug: executing borg create$' "\s--compression lz4\b"
# defined parameter
setconfig dest compression auto,zstd,13
testaction
greplog 'Debug: executing borg create$' "\s--compression auto,zstd,13\b"
}
@test "create local backup without encryption" {
# no encryption, no passphrase
setconfig dest archive testarchive
setconfig dest encryption none
delconfig dest passphrase
cleanup_backups local
runaction
greplog 'Debug: executing borg init$' 'Debug: borg init --encryption=none'
greplog "Warning: Repository has been initialized"
}
@test "verify local backup without encryption" {
unset BORG_PASSPHRASE
borg extract --dry-run "${BN_BACKUPDIR}/testborg::testarchive"
}
@test "create local backup with encryption" {
# encryption enabled, wrong passphrase
setconfig dest archive testarchive
setconfig dest encryption repokey
setconfig dest passphrase 123test
cleanup_backups local
runaction
greplog 'Debug: executing borg init$' 'Debug: borg init --encryption=repokey'
greplog "Warning: Repository has been initialized"
}
@test "verify local backup with encryption" {
export BORG_PASSPHRASE="123foo"
run borg extract --dry-run "${BN_BACKUPDIR}/testborg::testarchive"
[ "$status" -eq 2 ]
export BORG_PASSPHRASE="123test"
borg extract --dry-run "${BN_BACKUPDIR}/testborg::testarchive"
}
@test "create remote backup with encryption" {
setconfig dest archive testarchive
setconfig dest encryption repokey
setconfig dest passphrase 123test
setconfig dest host "${BN_REMOTEHOST}"
setconfig dest user "${BN_REMOTEUSER}"
cleanup_backups remote
runaction
greplog 'Debug: executing borg init$' 'Debug: borg init --encryption=repokey'
greplog "Warning: Repository has been initialized"
}
@test "verify remote backup with encryption" {
export BORG_PASSPHRASE="123foo"
run borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::testarchive"
[ "$status" -eq 2 ]
export BORG_PASSPHRASE="123test"
borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::testarchive"
}

244
test/common.bash Normal file
View File

@ -0,0 +1,244 @@
setup() {
# declare some constants
readonly BN_REMOTEUSER="vagrant"
readonly BN_REMOTEHOST="bntest1"
readonly BN_BACKUPDIR="/var/backups"
readonly BN_SRCDIR="/var/cache/bntest"
# Write a basic backupninja config file
cat << EOF > "${BATS_TMPDIR}/backupninja.conf"
when = manual
loglevel = 5
reportemail = root
reportsuccess = yes
reportinfo = no
reportwarning = yes
reportspace = no
reporthost =
reportuser = ninja
reportdirectory = /var/lib/backupninja/reports
admingroup = root
logfile = ${BATS_TMPDIR}/log/backupninja.log
configdirectory = ${BATS_TMPDIR}/backup.d
scriptdirectory = /usr/share/backupninja
libdirectory = /usr/lib/backupninja
usecolors = no
EOF
# Create backupninja directories
mkdir "${BATS_TMPDIR}/log" "${BATS_TMPDIR}/backup.d"
chmod 0750 "${BATS_TMPDIR}/backup.d"
# Get name of component being tested
COMP=$(basename -s .bats "${BATS_TEST_FILENAME}")
# Invoke component-specific general test setup
# (runs only before the first test case)
if [[ "$BATS_TEST_NUMBER" -eq 1 ]]; then
if type "begin_${COMP}" 2>&1 | grep -q "function"; then
begin_${COMP}
fi
fi
# Invoke component-specific test setup
if type "setup_${COMP}" 2>&1 | grep -q "function"; then
setup_${COMP}
fi
}
teardown() {
# Print the debug log in case the test case fails
if [ -f "${BATS_TMPDIR}/log/backupninja.log" ]; then
echo "cat ${BATS_TMPDIR}/log/backupninja.log :"
cat "${BATS_TMPDIR}/log/backupninja.log"
# Copy logfile so it can be examined in subsequent tests
cp "${BATS_TMPDIR}/log/backupninja.log" "${BATS_TMPDIR}/_backupninja.log"
else
echo "backupninja.log not found"
fi
# Clean up
rm -rf "${BATS_TMPDIR}/backupninja.conf" \
"${BATS_TMPDIR}/log" \
"${BATS_TMPDIR}/backup.d" \
/var/mail/vagrant
# Invoke component-specific test teardown
if type "teardown_${COMP}" 2>&1 | grep -q "function"; then
teardown_${COMP}
fi
# Invoke component-specific general test teardown
# (runs only after the last test case)
if [[ "${#BATS_TEST_NAMES[@]}" -eq "$BATS_TEST_NUMBER" ]]; then
if type "finish_${COMP}" 2>&1 | grep -q "function"; then
finish_${COMP}
fi
fi
}
# set parameter/value in action config file
setconfig() {
if [ -f "${BATS_TMPDIR}/$1" ]; then
local CONFIGFILE="${BATS_TMPDIR}/$1"
shift
else
local COMP=$(basename -s .bats "${BATS_TEST_FILENAME}")
local CONFIGFILE="${BATS_TMPDIR}/backup.d/test.${COMP}"
fi
if [ -n "$3" ]; then
# named section
crudini --set "$CONFIGFILE" "$1" "$2" "$3"
else
# default section
crudini --set "$CONFIGFILE" '' "$1" "$2"
fi
}
# special-case for repeating config parameters
# crudini doesn't support those
# (used for include and exclude parameters)
setconfig_repeat() {
if [ -f "${BATS_TMPDIR}/$1" ]; then
local CONFIGFILE="${BATS_TMPDIR}/$1"
shift
else
local COMP=$(basename -s .bats "${BATS_TEST_FILENAME}")
local CONFIGFILE="${BATS_TMPDIR}/backup.d/test.${COMP}"
fi
local SECTION="$1"
local PARAM="$2"
shift 2
crudini --del "$CONFIGFILE" "$SECTION" "$PARAM"
for v; do
crudini --set --list --list-sep=$'\nREPEAT = ' "$CONFIGFILE" "$SECTION" "$PARAM" "$v"
done
sed -i "s#^\s\+REPEAT =#${PARAM} =#" "$CONFIGFILE"
}
# delete config parameter
delconfig() {
if [ -f "${BATS_TMPDIR}/$1" ]; then
local CONFIGFILE="${BATS_TMPDIR}/$1"
shift
else
local COMP=$(basename -s .bats "${BATS_TEST_FILENAME}")
local CONFIGFILE="${BATS_TMPDIR}/backup.d/test.${COMP}"
fi
if [ -n "$2" ]; then
# named section
crudini --del "$CONFIGFILE" "$1" "$2"
else
# default section
crudini --del "$CONFIGFILE" '' "$1"
fi
}
# execute command on remote vagrant host
remote_command() {
ssh "${BN_REMOTEUSER}@${BN_REMOTEHOST}" "$1"
}
# remove backup test artifacts
cleanup_backups() {
for c in "$@"; do
case "$c" in
"local")
grep -q "^tmpfs $BN_BACKUPDIR" /proc/mounts && umount "$BN_BACKUPDIR"
mount -t tmpfs tmpfs "$BN_BACKUPDIR"
;;
"remote")
run remote_command "grep -q \"^tmpfs $BN_BACKUPDIR\" /proc/mounts && sudo umount \"$BN_BACKUPDIR\""
remote_command "sudo mount -t tmpfs tmpfs \"$BN_BACKUPDIR\""
;;
esac
done
}
# run backupninja action, removing previous log file if exists
runaction() {
# enable test mode?
if [ "$1" == "test" ]; then
local TEST="--test"
shift
else
local TEST=""
fi
# get component
if [ -n "$1" ]; then
local ACTIONFILE="$1"
else
local ACTIONFILE="test.$(basename -s .bats "${BATS_TEST_FILENAME}")"
fi
# run action
if [ -f "${BATS_TMPDIR}/backup.d/${ACTIONFILE}" ]; then
[ -f "${BATS_TMPDIR}/log/backupninja.log" ] && rm -f "${BATS_TMPDIR}/log/backupninja.log"
run backupninja -f "${BATS_TMPDIR}/backupninja.conf" $TEST --debug --now --run "${BATS_TMPDIR}/backup.d/${ACTIONFILE}"
[ "$status" -eq 0 ]
else
echo "action file not found: ${BATS_TMPDIR}/backup.d/${ACTIONFILE}"
false
fi
}
# run backupninja action in test mode, removing previous log file if exist
testaction() {
runaction test "$1"
}
# grep the backupninja log
greplog() {
if [ -z "$2" ]; then
grep -q "$1" "${BATS_TMPDIR}/log/backupninja.log"
else
# grep line following previous match
grep -A1 "$1" "${BATS_TMPDIR}/log/backupninja.log" | tail -n1 | grep -q -- "$2"
fi
}
not_greplog() {
if [ -z "$2" ]; then
! grep -q "$1" "${BATS_TMPDIR}/log/backupninja.log"
else
# grep line following previous match
! (grep -A1 "$1" "${BATS_TMPDIR}/log/backupninja.log" | tail -n1 | grep -q -- "$2")
fi
}
makegpgkeys() {
# encryption key
run gpg --keyid-format long -k encrypt@bntest0 2>/dev/null
if [ "$status" -eq 2 ]; then
gpg --batch --gen-key <<" EOF"
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: Encrypt key
Name-Email: encrypt@bntest0
Expire-Date: 0
Passphrase: 123encrypt
EOF
fi
BN_ENCRYPTKEY=$(gpg --keyid-format long -k encrypt@bntest0 | sed -n '2p' | grep -o '\S\+')
export BN_ENCRYPTKEY
# signing key
run gpg --keyid-format long -k sign@bntest0 2>/dev/null
if [ "$status" -eq 2 ]; then
gpg --batch --gen-key <<" EOF"
Key-Type: 1
Key-Length: 2048
Subkey-Type: 1
Subkey-Length: 2048
Name-Real: Sign key
Name-Email: sign@bntest0
Expire-Date: 0
Passphrase: 123sign
EOF
fi
BN_SIGNKEY=$(gpg --keyid-format long -k sign@bntest0 | sed -n '2p' | grep -o '\S\+')
export BN_SIGNKEY
}

454
test/dup.bats Normal file
View File

@ -0,0 +1,454 @@
load common
begin_dup() {
apt-get -qq install debootstrap duplicity trickle
if [ ! -d /var/cache/bntest ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR"
fi
}
setup_dup() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.dup"
when = manual
options =
nicelevel = 0
testconnect = no
ionicelevel =
tmpdir = /tmp
[gpg]
password = 123test
signpassword =
sign =
encryptkey =
signkey =
[source]
include = ${BN_SRCDIR}
exclude = ${BN_SRCDIR}/var
[dest]
incremental = yes
increments = 30
keep = yes
keepincroffulls = all
desturl = file:///${BN_BACKUPDIR}/testdup
sshoptions =
bandwidthlimit = 0
desthost =
destdir = ${BN_BACKUPDIR}/testdup
destuser =
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.dup"
# reset duplicity archive-dir
# default path set by handler is /var/cache/backupninja
BN_DUPARCHIVEDIR="/var/cache/backupninja/duplicity"
export BN_DUPARCHIVEDIR
[ -d "$BN_DUPARCHIVEDIR" ] && rm -rf "$BN_DUPARCHIVEDIR"
mkdir -p "$BN_DUPARCHIVEDIR"
}
finish_dup() {
cleanup_backups local remote
# cleanup duplicity cache dir
rm -rf /var/cache/backupninja
remote_command "sudo rm -rf /var/cache/backupninja"
# cleanup test gpg keys
gpg --list-secret-keys \*@bntest0 | grep -e '^\s\+[A-Z0-9]\{40\}' | tr -d ' ' | xargs -L1 sudo gpg --delete-secret-keys --batch --no-tty --yes
gpg --list-keys \*@bntest0 | grep -e '^\s\+[A-Z0-9]\{40\}' | tr -d ' ' | xargs -L1 sudo gpg --delete-keys --batch --no-tty --yes
}
@test "check ssh connection test" {
setconfig testconnect yes
setconfig dest destuser $BN_REMOTEUSER
setconfig dest desthost $BN_REMOTEHOST
delconfig dest desturl
testaction
greplog "Debug: Connected to ${BN_REMOTEHOST} as ${BN_REMOTEUSER} successfully$"
}
@test "check config parameter nicelevel" {
# nicelevel is 0 by default
delconfig nicelevel
testaction
greplog 'Debug: executing duplicity$' '\bnice -n 0\b'
# nicelevel is defined
setconfig nicelevel -19
testaction
greplog 'Debug: executing duplicity$' '\bnice -n -19\b'
}
@test "check config parameter ionicelevel" {
# no ionice by default
delconfig ionicelevel
testaction
not_greplog 'Debug: executing duplicity$' '\bionice -c2\b'
# acceptable value
setconfig ionicelevel 7
testaction
greplog 'Debug: executing duplicity$' '\bionice -c2 -n 7\b'
# unacceptable value
setconfig ionicelevel 10
testaction
greplog 'Fatal: The value of ionicelevel is expected to be either empty or an integer from 0 to 7. Got: 10$'
}
@test "check config parameter options" {
setconfig options "--verbosity 8"
testaction
greplog 'Debug: executing duplicity$' '\s--verbosity 8\b'
}
@test "check config parameter tmpdir" {
# tmpdir undefined
delconfig tmpdir
testaction
not_greplog 'Debug: executing duplicity$' '\s--tmpdir\b'
# tmpdir defined
setconfig tmpdir /tmp
testaction
not_greplog 'Debug: executing duplicity$' '\s--tmpdir /tmp\b'
}
@test "check config parameter source/include" {
# missing path
delconfig source include
testaction
greplog 'Fatal: No source includes specified.$'
# single path
setconfig source include "$BN_SRCDIR"
testaction
greplog 'Debug: executing duplicity$' "\s--include '${BN_SRCDIR}'"
# multiple paths
setconfig_repeat source include "$BN_SRCDIR" /foo /bar
testaction
greplog 'Debug: executing duplicity$' "\s--include '${BN_SRCDIR}' --include '/foo' --include '/bar'\s"
}
@test "check config parameter source/exclude" {
# absent path
delconfig source exclude
testaction
greplog 'Debug: executing duplicity$' "\s--include '${BN_SRCDIR}' --exclude '\*\*' /\s"
# single path
setconfig source exclude "${BN_SRCDIR}/var"
testaction
greplog 'Debug: executing duplicity$' "\s--exclude '${BN_SRCDIR}/var'\s"
# multiple paths
setconfig_repeat source exclude "$BN_SRCDIR/var" "$BN_SRCDIR/foo" "$BN_SRCDIR/bar"
testaction
greplog 'Debug: executing duplicity$' "\s--exclude '${BN_SRCDIR}/var' --exclude '${BN_SRCDIR}/foo' --exclude '${BN_SRCDIR}/bar'\s"
}
@test "check config parameter dest/incremental" {
# absent parameter, defaults to yes
delconfig dest incremental
testaction
greplog 'Debug: executing duplicity$' 'Debug: nice -n 0 LC_ALL=C duplicity --no-print-statistics'
# defined, set to yes
setconfig dest incremental yes
testaction
greplog 'Debug: executing duplicity$' 'Debug: nice -n 0 LC_ALL=C duplicity --no-print-statistics'
# defined, set to no
setconfig dest incremental no
testaction
greplog 'Debug: executing duplicity$' 'Debug: nice -n 0 LC_ALL=C duplicity full --no-print-statistics'
}
@test "check config parameter dest/increments" {
# absent parameter, defaults to 30
delconfig dest increments
testaction
greplog 'Debug: executing duplicity$' '\s--full-if-older-than 30D\b'
# defined, set to 60
setconfig dest increments 60
testaction
greplog 'Debug: executing duplicity$' '\s--full-if-older-than 60D\b'
# defined, set to keep
setconfig dest increments keep
testaction
not_greplog 'Debug: executing duplicity$' '\s--full-if-older-than\s'
}
@test "check config parameter dest/keep" {
# absent parameter, defaults to 60
delconfig dest keep
testaction
greplog 'Debug: executing duplicity remove-older-than$' '\sduplicity remove-older-than 60D\b'
# defined, set to 180
setconfig dest keep 180
testaction
greplog 'Debug: executing duplicity remove-older-than$' '\sduplicity remove-older-than 180D\b'
# defined, set to yes
setconfig dest keep yes
testaction
not_greplog 'Debug: executing duplicity remove-older-than$'
}
@test "check config parameter dest/keepincroffulls" {
# absent parameter, defaults to all
setconfig dest keep 30
delconfig dest keepincroffulls
testaction
not_greplog 'Debug: executing duplicity remove-all-inc-of-but-n-full$'
# defined, set to 1
setconfig dest keep 30
setconfig dest keepincroffulls 1
testaction
greplog 'Debug: executing duplicity remove-all-inc-of-but-n-full$' '\sduplicity remove-all-inc-of-but-n-full 1\b'
# defined, set to all
setconfig dest keep 30
setconfig dest keepincroffulls all
testaction
not_greplog 'Debug: executing duplicity remove-all-inc-of-but-n-full$'
}
@test "check config parameter dest/awsaccesskeyid" {
skip "not implemented"
}
@test "check config parameter dest/awssecretaccesskey" {
skip "not implemented"
}
@test "check config parameter dest/cfusername" {
skip "not implemented"
}
@test "check config parameter dest/cfapikey" {
skip "not implemented"
}
@test "check config parameter dest/cfauthurl" {
skip "not implemented"
}
@test "check config parameter dest/dropboxappkey" {
skip "not implemented"
}
@test "check config parameter dest/dropboxappsecret" {
skip "not implemented"
}
@test "check config parameter dest/dropboxaccesstoken" {
skip "not implemented"
}
@test "check config parameter dest/ftp_password" {
skip "not implemented"
}
@test "check config parameter dest/sshoptions" {
# undefined
delconfig dest sshoptions
testaction
greplog 'Debug: executing duplicity$' "\s--ssh-options ''\s"
# defined
setconfig dest sshoptions "-oIdentityFile=/root/.ssh/id_rsa"
testaction
greplog 'Debug: executing duplicity$' "\s--ssh-options '-oIdentityFile=/root/.ssh/id_rsa'\s"
}
@test "check config parameter dest/bandwidthlimit" {
# undefined, disabled by default
delconfig dest bandwidthlimit
testaction
not_greplog "\btrickle -s\b"
# defined, set to 250, local file path
setconfig dest bandwidthlimit 250
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
testaction
greplog 'Warning: The bandwidthlimit option is not used with a local file path destination.'
not_greplog 'Debug: executing duplicity$' "\strickle -s -d 250 -u 250 duplicity\s"
# defined, set to 250, remote path
setconfig dest bandwidthlimit 250
setconfig dest desturl "sftp://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testdup"
testaction
greplog 'Debug: executing duplicity$' "\strickle -s -d 250 -u 250 duplicity\s"
}
@test "check config parameter dest/desturl" {
# undefined desturl
delconfig dest desturl
delconfig dest desthost
testaction
greplog 'Fatal: The destination host (desthost) must be set when desturl is not used.$'
# desturl, file protocol
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
testaction
greplog 'Debug: executing duplicity$' "\sfile://${BN_BACKUPDIR}/testdup$"
# desturl, sftp protocol
setconfig dest desturl "sftp://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testdup"
testaction
greplog 'Debug: executing duplicity$' "\ssftp://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testdup$"
}
@test "check config parameters dest/desthost, dest/destuser, dest/destdir" {
delconfig dest desturl
setconfig dest desthost "$BN_REMOTEHOST"
setconfig dest destuser "$BN_REMOTEUSER"
setconfig dest destdir "$BN_BACKUPDIR/testdup"
testaction
greplog 'Debug: executing duplicity$' "\sscp://${BN_REMOTEUSER}@${BN_REMOTEHOST}/${BN_BACKUPDIR}/testdup$"
}
@test "create local backup with symmetric encryption" {
cleanup_backups local
mkdir -p /var/backups/testdup
setconfig gpg password 123test
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
delconfig dest destdir
runaction
greplog "Debug: Data will be encrypted using symmetric encryption."
greplog "Info: Duplicity finished successfully."
}
@test "verify local backup with symmetric encryption" {
export PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
export PASSPHRASE="123test"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}
@test "create local backup with public key encryption, unsigned" {
makegpgkeys
cleanup_backups local
mkdir -p /var/backups/testdup
setconfig gpg encryptkey "$BN_ENCRYPTKEY"
setconfig gpg password 123test
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
delconfig dest destdir
runaction
greplog "Debug: Data will be encrypted with the GnuPG key $BN_ENCRYPTKEY.$"
greplog "Debug: Data won't be signed."
greplog "Info: Duplicity finished successfully."
}
@test "verify local backup with public key encryption, unsigned" {
gpgconf --reload gpg-agent && export PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
echo "$output" | grep -q "gpg: public key decryption failed: Bad passphrase"
gpgconf --reload gpg-agent && export PASSPHRASE="123encrypt"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}
@test "create local backup with public key encryption, signed with same key" {
makegpgkeys
cleanup_backups local
mkdir -p /var/backups/testdup
setconfig gpg encryptkey "$BN_ENCRYPTKEY"
setconfig gpg password 123encrypt
setconfig gpg sign yes
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
delconfig dest destdir
runaction
greplog "Debug: Data will be encrypted ang signed with the GnuPG key ${BN_ENCRYPTKEY}.$"
greplog "Info: Duplicity finished successfully."
}
@test "verify local backup with public key encryption, signed with same key" {
gpgconf --reload gpg-agent && export PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
echo "$output" | grep -q "gpg: public key decryption failed: Bad passphrase"
gpgconf --reload gpg-agent && export PASSPHRASE="123encrypt"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}
@test "create local backup with public key encryption, signed with different key" {
makegpgkeys
cleanup_backups local
mkdir -p /var/backups/testdup
setconfig gpg encryptkey "$BN_ENCRYPTKEY"
setconfig gpg password 123encrypt
setconfig gpg sign yes
setconfig gpg signkey "$BN_SIGNKEY"
setconfig gpg signpassword 123sign
setconfig dest desturl "file://${BN_BACKUPDIR}/testdup"
delconfig dest destdir
runaction
greplog "Debug: Data will be encrypted with the GnuPG key ${BN_ENCRYPTKEY}.$"
greplog "Debug: Data will be signed with the GnuPG key ${BN_SIGNKEY}.$"
greplog "Info: Duplicity finished successfully."
}
@test "verify local backup with public key encryption, signed with different key" {
gpgconf --reload gpg-agent && export PASSPHRASE="123foo" SIGN_PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
echo "$output" | grep -q "gpg: public key decryption failed: Bad passphrase"
gpgconf --reload gpg-agent && export PASSPHRASE="123encrypt" SIGN_PASSPHRASE="123sign"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "file://${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}
@test "create remote backup with symmetric encryption" {
setconfig gpg password 123test
delconfig dest desturl
setconfig dest destuser "$BN_REMOTEUSER"
setconfig dest desthost "$BN_REMOTEHOST"
setconfig dest destdir "${BN_BACKUPDIR}/testdup"
cleanup_backups remote
runaction
greplog "Debug: Data will be encrypted using symmetric encryption."
greplog "Info: Duplicity finished successfully."
}
@test "verify remote backup with symmetric encryption" {
export PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "scp://${BN_REMOTEUSER}@$BN_REMOTEHOST/${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
export PASSPHRASE="123test"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "scp://${BN_REMOTEUSER}@$BN_REMOTEHOST/${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}
@test "create remote backup with public key encryption, signed with same key" {
makegpgkeys
setconfig gpg encryptkey "$BN_ENCRYPTKEY"
setconfig gpg password 123encrypt
setconfig gpg sign yes
delconfig dest desturl
setconfig dest destuser "$BN_REMOTEUSER"
setconfig dest desthost "$BN_REMOTEHOST"
setconfig dest destdir "${BN_BACKUPDIR}/testdup"
cleanup_backups remote
runaction
greplog "Debug: Data will be encrypted ang signed with the GnuPG key ${BN_ENCRYPTKEY}.$"
greplog "Info: Duplicity finished successfully."
}
@test "verify remote backup with public key encryption, signed with same key" {
gpgconf --reload gpg-agent && export PASSPHRASE="123foo"
run duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "scp://${BN_REMOTEUSER}@$BN_REMOTEHOST/${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
[ "$status" -eq 31 ]
echo "$output" | grep -q "gpg: public key decryption failed: Bad passphrase"
gpgconf --reload gpg-agent && export PASSPHRASE="123encrypt"
duplicity verify --archive-dir "$BN_DUPARCHIVEDIR" "scp://${BN_REMOTEUSER}@$BN_REMOTEHOST/${BN_BACKUPDIR}/testdup" "$BN_SRCDIR"
}

128
test/mysql.bats Normal file
View File

@ -0,0 +1,128 @@
load common
begin_mysql() {
apt-get -qq install default-mysql-server
systemctl is-active mysql || systemctl start mysql
zcat "${BATS_TEST_DIRNAME}/samples/bntest_p8Cz8k.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf
zcat "${BATS_TEST_DIRNAME}/samples/bntest_v11vJj.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf
}
setup_mysql() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.mysql"
databases = all
backupdir = ${BN_BACKUPDIR}/mysql
hotcopy = no
sqldump = yes
compress = yes
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.mysql"
}
finish_mysql() {
mysqladmin -f drop bntest_p8Cz8k
mysqladmin -f drop bntest_v11vJj
systemctl stop mysql
}
teardown_mysql() {
cleanup_backups local
}
@test "sqldump: exports all databases, with compression" {
setconfig databases all
setconfig compress yes
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz" ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz)" -eq 9 ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 47 ]
}
@test "sqldump: exports all databases, without compression" {
setconfig databases all
setconfig compress no
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql" ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql" ]
[ "$(grep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql)" -eq 9 ]
[ "$(grep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 47 ]
}
@test "sqldump: exports specific database" {
setconfig databases bntest_v11vJj
runaction
[ ! -f ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 47 ]
}
@test "ignores: exports all databases while excluding two tables entirely" {
setconfig databases all
setconfig ignores "bntest_v11vJj.cache_data bntest_v11vJj.cache_entity"
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz)" -eq 9 ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz" ]
[ "$(zgrep -c 'CREATE TABLE `cache_data`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 0 ]
[ "$(zgrep -c 'CREATE TABLE `cache_entity`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 0 ]
[ "$(zgrep -c 'CREATE TABLE' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 66 ]
}
@test "nodata: exports all databases while excluding data from one table, with compression" {
setconfig databases all
setconfig compress yes
setconfig nodata bntest_v11vJj.cache_data
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz)" -eq 9 ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO `cache_data`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 0 ]
[ "$(zgrep -c 'CREATE TABLE' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 68 ]
}
@test "nodata: exports all databases while excluding data from one table, without compression" {
setconfig databases all
setconfig compress no
setconfig nodata bntest_v11vJj.cache_data
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql" ]
[ "$(grep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql)" -eq 9 ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql" ]
[ "$(grep -c 'INSERT INTO `cache_data`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 0 ]
[ "$(grep -c 'CREATE TABLE' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 68 ]
}
@test "nodata: exports all databases while excluding data from two tables, with compression" {
setconfig databases all
setconfig compress yes
setconfig nodata "bntest_v11vJj.cache_data bntest_v11vJj.cache_entity"
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql.gz)" -eq 9 ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz" ]
[ "$(zgrep -c 'INSERT INTO `cache_data`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 0 ]
[ "$(zgrep -c 'INSERT INTO `cache_entity`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 0 ]
[ "$(zgrep -c 'CREATE TABLE' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql.gz)" -eq 68 ]
}
@test "nodata: exports all databases while excluding data from two tables, without compression" {
setconfig databases all
setconfig compress no
setconfig nodata "bntest_v11vJj.cache_data bntest_v11vJj.cache_entity"
runaction
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql" ]
[ "$(grep -c 'INSERT INTO' ${BN_BACKUPDIR}/mysql/sqldump/bntest_p8Cz8k.sql)" -eq 9 ]
[ -s "${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql" ]
[ "$(grep -c 'INSERT INTO `cache_data`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 0 ]
[ "$(grep -c 'INSERT INTO `cache_entity`' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 0 ]
[ "$(grep -c 'CREATE TABLE' ${BN_BACKUPDIR}/mysql/sqldump/bntest_v11vJj.sql)" -eq 68 ]
}
@test "hotcopy: exports all databases" {
skip "not implemented, requires MyISAM tables, method is deprecated upstream"
}
@test "hotcopy: exports specific databases" {
skip "not implemented, requires MyISAM tables, method is deprecated upstream"
}

157
test/pgsql.bats Normal file
View File

@ -0,0 +1,157 @@
load common
begin_pgsql() {
apt-get -qq install postgresql
systemctl is-active postgresql || systemctl start postgresql
sudo -u postgres createuser --superuser root
createdb bntest_p8Cz8k
createdb bntest_v11vJj
zcat "${BATS_TEST_DIRNAME}/samples/bntest_p8Cz8k.psql.gz" | psql -d bntest_p8Cz8k
zcat "${BATS_TEST_DIRNAME}/samples/bntest_v11vJj.psql.gz" | psql -d bntest_v11vJj
}
setup_pgsql() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.pgsql"
databases = all
backupdir = ${BN_BACKUPDIR}/postgres
compress = yes
format = plain
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.pgsql"
}
finish_pgsql() {
dropdb bntest_p8Cz8k
dropdb bntest_v11vJj
sudo -u postgres dropuser root
systemctl stop postgresql
}
teardown_pgsql() {
cleanup_backups local
}
@test "plain: exports all databases, with compression" {
setconfig databases all
setconfig compress yes
setconfig format plain
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest0-all.sql.gz"
}
@test "plain: exports all databases, without compression" {
setconfig databases all
setconfig compress no
setconfig format plain
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest0-all.sql" ]
}
@test "plain: exports specific database, with compression" {
setconfig databases bntest_v11vJj
setconfig compress yes
setconfig format plain
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_v11vJj.sql.gz"
[ "$(zgrep -c -e '^COPY' ${BN_BACKUPDIR}/postgres/bntest_v11vJj.sql.gz)" -eq 68 ]
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.sql.gz" ]
}
@test "plain: exports specific database, without compression" {
setconfig databases bntest_v11vJj
setconfig compress no
setconfig format plain
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest_v11vJj.sql" ]
[ "$(grep -c -e '^COPY' ${BN_BACKUPDIR}/postgres/bntest_v11vJj.sql)" -eq 68 ]
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.sql" ]
}
@test "tar: exports all databases, with compression" {
setconfig databases all
setconfig compress yes
setconfig format tar
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump.gz"
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump.gz"
gzip -tq "${BN_BACKUPDIR}/postgres/globals.sql.gz"
}
@test "tar: exports all databases, without compression" {
setconfig databases all
setconfig compress no
setconfig format tar
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump" ]
[ -s "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" ]
file ${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump | grep "POSIX tar archive"
file ${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump | grep "POSIX tar archive"
[ -s "${BN_BACKUPDIR}/postgres/globals.sql" ]
}
@test "tar: exports specific database, with compression" {
setconfig databases bntest_v11vJj
setconfig compress yes
setconfig format tar
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump.gz"
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump.gz" ]
[ ! -e "${BN_BACKUPDIR}/postgres/globals.sql.gz" ]
}
@test "tar: exports specific database, without compression" {
setconfig databases bntest_v11vJj
setconfig compress no
setconfig format tar
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" ]
file ${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump | grep -q "POSIX tar archive"
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump" ]
[ ! -e "${BN_BACKUPDIR}/postgres/globals.sql" ]
}
@test "custom: exports all databases, with compression" {
setconfig databases all
setconfig compress yes
setconfig format custom
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump.gz"
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump.gz"
gzip -tq "${BN_BACKUPDIR}/postgres/globals.sql.gz"
}
@test "custom: exports all databases, without compression" {
setconfig databases all
setconfig compress no
setconfig format custom
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump" ]
[ -s "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" ]
file "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump" | grep -q "PostgreSQL custom database dump"
file "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" | grep -q "PostgreSQL custom database dump"
[ -s "${BN_BACKUPDIR}/postgres/globals.sql" ]
}
@test "custom: exports specific database, with compression" {
setconfig databases bntest_v11vJj
setconfig compress yes
setconfig format custom
runaction
gzip -tq "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump.gz"
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump.gz" ]
[ ! -e "${BN_BACKUPDIR}/postgres/globals.sql.gz" ]
}
@test "custom: exports specific database, without compression" {
setconfig databases bntest_v11vJj
setconfig compress no
setconfig format custom
runaction
[ -s "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" ]
file "${BN_BACKUPDIR}/postgres/bntest_v11vJj.pg_dump" | grep -q "PostgreSQL custom database dump"
[ ! -e "${BN_BACKUPDIR}/postgres/bntest_p8Cz8k.pg_dump" ]
[ ! -e "${BN_BACKUPDIR}/postgres/globals.sql" ]
}

428
test/rdiff.bats Normal file
View File

@ -0,0 +1,428 @@
load common
begin_rdiff() {
apt-get -qq install debootstrap rdiff-backup cstream
if [ ! -d /var/cache/bntest ]; then
debootstrap --variant=minbase testing /var/cache/bntest
fi
}
setup_rdiff() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.rdiff"
when = manual
options =
nicelevel =
testconnect = no
bwlimit =
ignore_version =
output_as_info =
keep = yes
[source]
label = testrdiff
type = local
keep = yes
include = ${BN_SRCDIR}
exclude = ${BN_SRCDIR}/var
[dest]
type = local
directory = $BN_BACKUPDIR
host =
user =
sshoptions =
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.rdiff"
}
finish_rdiff() {
cleanup_backups local remote
}
@test "check ssh connection test" {
setconfig testconnect yes
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
greplog "Debug: Connected to ${BN_REMOTEHOST} as ${BN_REMOTEUSER} successfully$"
}
@test "check config parameter nicelevel" {
# nicelevel is 0 by default
delconfig nicelevel
testaction
greplog 'Debug: executing rdiff-backup$' '\bnice -n 0\b'
# nicelevel is defined
setconfig nicelevel 19
testaction
greplog 'Debug: executing rdiff-backup$' '\bnice -n 19\b'
}
@test "check config parameter ionicelevel" {
# no ionice by default
delconfig ionicelevel
testaction
not_greplog 'Debug: executing rdiff-backup$' '\bionice -c2\b'
# acceptable value
setconfig ionicelevel 7
testaction
greplog 'Debug: executing rdiff-backup$' '\bionice -c2 -n 7\b'
# unacceptable value
setconfig ionicelevel 10
testaction
greplog 'Fatal: The value of ionicelevel is expected to be either empty or an integer from 0 to 7. Got: 10$'
}
@test "check config parameter bwlimit" {
# no limit by default
delconfig bwlimit
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
not_greplog 'Debug: executing rdiff-backup$' '\bcstream -t\b'
# limit is defined
setconfig bwlimit 1024000
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
greplog 'Debug: executing rdiff-backup$' '\bcstream -t 1024000\b'
}
@test "check config parameter ignore_version" {
# undefined, defaults to no
delconfig ignore_version
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
greplog 'Debug: executing rdiff-backup version checks$'
greplog 'Debug: source version: rdiff-backup '
greplog 'Debug: destination version: rdiff-backup '
not_greplog 'Fatal: rdiff-backup does not have the same version at the source and at the destination.$'
# defined, set to no
setconfig ignore_version no
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
greplog 'Debug: executing rdiff-backup version checks$'
greplog 'Debug: source version: rdiff-backup '
greplog 'Debug: destination version: rdiff-backup '
not_greplog 'Fatal: rdiff-backup does not have the same version at the source and at the destination.$'
# defined, set to yes
setconfig ignore_version yes
setconfig dest type remote
setconfig dest user $BN_REMOTEUSER
setconfig dest host $BN_REMOTEHOST
testaction
not_greplog 'Debug: executing rdiff-backup version checks$'
not_greplog 'Debug: source version: rdiff-backup '
not_greplog 'Debug: destination version: rdiff-backup '
not_greplog 'Fatal: rdiff-backup does not have the same version at the source and at the destination.$'
}
@test "check config parameter options" {
# undefined, default empty
delconfig options
testaction
greplog 'Debug: executing rdiff-backup$' "\brdiff-backup\s\+--print-statistics\s"
# defined, set to --tempdir /tmp
setconfig options "--tempdir /tmp"
testaction
greplog 'Debug: executing rdiff-backup$' "\brdiff-backup\s\+--tempdir /tmp --print-statistics\s"
}
@test "check config parameter source/type" {
# undefined, defaults empty
delconfig source type
testaction
greplog "Fatal: sourcetype '' is neither local nor remote$"
# defined, set to local
setconfig source type local
testaction
not_greplog "Fatal: sourcetype '' is neither local nor remote$"
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
# defined, set to remote
setconfig source type remote
setconfig source user "$BN_REMOTEUSER"
setconfig source host "$BN_REMOTEHOST"
testaction
not_greplog "Fatal: sourcetype '' is neither local nor remote$"
greplog 'Debug: executing rdiff-backup$' "\s${BN_REMOTEUSER}@${BN_REMOTEHOST}::/ ${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameter source/label" {
# undefined, defaults empty
delconfig source label
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/$"
# defined, set to testrdiff
setconfig source label testrdiff
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameters source/user and source/host" {
# user undefined, type remote, defaults empty
setconfig source type remote
delconfig source user
setconfig source host "$BN_REMOTEHOST"
testaction
greplog 'Fatal: User must be specified for remote source.'
# host undefined, type remote, defaults empty
setconfig source type remote
setconfig source user "$BN_REMOTEUSER"
delconfig source host
testaction
greplog 'Fatal: Host must be specified for remote source.'
# user/host undefined, type local, defaults empty (noop)
setconfig source type local
delconfig source user
delconfig source host
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
# defined, type remote
setconfig source type remote
setconfig source user "$BN_REMOTEUSER"
setconfig source host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s${BN_REMOTEUSER}@${BN_REMOTEHOST}::/ ${BN_BACKUPDIR}/testrdiff$"
# defined, type local (noop)
setconfig source type local
setconfig source user "$BN_REMOTEUSER"
setconfig source host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameter source/keep" {
# undefined, defaults to 60
delconfig source keep
testaction
greplog 'Debug: executing rdiff-backup --remove-older-than$' '\s--remove-older-than 60D\s'
# defined, set to 180
testaction
setconfig source keep 180
testaction
greplog 'Debug: executing rdiff-backup --remove-older-than$' '\s--remove-older-than 180D\s'
# defined, set to 6M
testaction
setconfig source keep 6M
testaction
greplog 'Debug: executing rdiff-backup --remove-older-than$' '\s--remove-older-than 6M\s'
# defined, set to 1 year
testaction
setconfig source keep "1 year"
testaction
greplog 'Fatal: Keep parameter contains an invalid value (1 year).$'
# defined, set to yes
testaction
setconfig source keep yes
testaction
not_greplog 'Fatal: Keep parameter contains an invalid value (yes).$'
not_greplog 'Debug: executing rdiff-backup --remove-older-than$'
}
@test "check config parameter source/include" {
# no includes, defaults source path to "/"
delconfig source include
delconfig source exclude
testaction
not_greplog 'Debug: executing rdiff-backup$' "\s--include"
greplog 'Debug: executing rdiff-backup$' "\s--print-statistics / ${BN_BACKUPDIR}/testrdiff$"
# single path, invalid
setconfig source include /
testaction
greplog "Fatal: Sorry, you cannot use 'include = /'$"
# single path
setconfig source include "$BN_SRCDIR"
testaction
greplog 'Debug: executing rdiff-backup$' "\s--include '${BN_SRCDIR}' --exclude '/\*' /\s"
# multiple paths
setconfig_repeat backup.d/test.rdiff source include "$BN_SRCDIR" /foo /bar
testaction
greplog 'Debug: executing rdiff-backup$' "\s--include '${BN_SRCDIR}' --include '/foo' --include '/bar' --exclude '/\*' /\s"
# regular path and filelist
setconfig_repeat backup.d/test.rdiff source include "$BN_SRCDIR" "@/etc/backup-list.txt"
testaction
greplog 'Debug: executing rdiff-backup$' "\s--include '${BN_SRCDIR}' --include-globbing-filelist '/etc/backup-list.txt' --exclude '/\*' /\s"
}
@test "check config parameter source/exclude" {
# no excludes, defaults source path to "/"
delconfig source exclude
testaction
greplog 'Debug: executing rdiff-backup$' "\s--print-statistics --include '${BN_SRCDIR}' --exclude '/\*' / ${BN_BACKUPDIR}/testrdiff$"
# single path
setconfig source exclude "${BN_SRCDIR}/foo"
testaction
greplog 'Debug: executing rdiff-backup$' "\s--exclude '${BN_SRCDIR}/foo' --include '${BN_SRCDIR}' --exclude '/\*' /\s"
# multiple paths
setconfig_repeat backup.d/test.rdiff source exclude "${BN_SRCDIR}/foo" "${BN_SRCDIR}/bar"
testaction
greplog 'Debug: executing rdiff-backup$' "\s--exclude '${BN_SRCDIR}/foo' --exclude '${BN_SRCDIR}/bar' --include '${BN_SRCDIR}' --exclude '/\*' /\s"
# regular path and filelist
setconfig_repeat backup.d/test.rdiff source exclude "${BN_SRCDIR}/foo" "@/etc/backup-exlist.txt"
testaction
greplog 'Debug: executing rdiff-backup$' "\s--exclude '${BN_SRCDIR}/foo' --exclude-globbing-filelist '/etc/backup-exlist.txt' --include '${BN_SRCDIR}' --exclude '/\*' /\s"
}
@test "check config parameter dest/type" {
# undefined, defaults empty
delconfig dest type
testaction
greplog "Fatal: desttype '' is neither local nor remote$"
# defined, set to local
setconfig dest type local
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
# defined, set to remote
setconfig dest type remote
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_REMOTEUSER}@${BN_REMOTEHOST}::${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameters dest/user and dest/host" {
# user undefined, type remote, defaults empty
setconfig dest type remote
delconfig dest user
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Fatal: User must be specified for remote destination.'
# host undefined, type remote, defaults empty
setconfig dest type remote
setconfig dest user "$BN_REMOTEUSER"
delconfig dest host
testaction
greplog 'Fatal: Host must be specified for remote destination.'
# user/host undefined, type local, defaults empty (noop)
setconfig dest type local
delconfig dest user
delconfig dest host
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
# defined, type remote
setconfig dest type remote
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_REMOTEUSER}@${BN_REMOTEHOST}::${BN_BACKUPDIR}/testrdiff$"
# defined, type local (noop)
setconfig dest type local
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameter dest/directory" {
# undefined, defaults empty
delconfig dest directory
testaction
greplog "Fatal: Destination directory not set$"
# defined, type local
setconfig dest type local
setconfig dest directory "$BN_BACKUPDIR"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_BACKUPDIR}/testrdiff$"
# defined, type remote, set to $BN_BACKUPDIR
setconfig dest type remote
setconfig dest directory "$BN_BACKUPDIR"
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\s/ ${BN_REMOTEUSER}@${BN_REMOTEHOST}::${BN_BACKUPDIR}/testrdiff$"
}
@test "check config parameter dest/sshoptions" {
# undefined, default empty
delconfig dest sshoptions
setconfig dest type remote
setconfig dest directory "$BN_BACKUPDIR"
setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST"
testaction
greplog 'Debug: executing rdiff-backup$' "\brdiff-backup\s\+--print-statistics\s"
# defined, set to -4
setconfig dest sshoptions "-o IdentityFile=/root/.ssh/id_ed25519"
testaction
greplog 'Debug: executing rdiff-backup$' "\brdiff-backup\s\+--remote-schema 'ssh -C -o IdentityFile=/root/.ssh/id_ed25519 %s rdiff-backup --server'\s"
}
@test "create local backup" {
cleanup_backups local
runaction
greplog "Info: Successfully finished backing up source testrdiff$"
}
@test "verify local backup" {
rdiff-backup -v5 --verify "${BN_BACKUPDIR}/testrdiff" > "${BATS_TMPDIR}/_rdiff-verify.log"
}
@test "verify number of files in local backup matches source" {
SRC_FILES="$(find "${BN_SRCDIR}" -type f -not -path "${BN_SRCDIR}/var/*" | wc -l)"
BACKUP_FILES="$(grep '^Verified SHA1 digest of' "${BATS_TMPDIR}/_rdiff-verify.log" | wc -l)"
[ "$SRC_FILES" -eq "$BACKUP_FILES" ]
}
@test "create remote backup" {
cleanup_backups remote
setconfig dest type remote
setconfig dest host "$BN_REMOTEHOST"
setconfig dest user "$BN_REMOTEUSER"
runaction
greplog "Info: Successfully finished backing up source testrdiff$"
}
@test "verify remote backup" {
rdiff-backup -v5 --verify "${BN_BACKUPDIR}/testrdiff" > "${BATS_TMPDIR}/_rdiff-verify.log"
}
@test "verify number of files in remote backup matches source" {
SRC_FILES="$(find "${BN_SRCDIR}" -type f -not -path "${BN_SRCDIR}/var/*" | wc -l)"
BACKUP_FILES="$(grep '^Verified SHA1 digest of' "${BATS_TMPDIR}/_rdiff-verify.log" | wc -l)"
[ "$SRC_FILES" -eq "$BACKUP_FILES" ]
}

148
test/rsync.bats Normal file
View File

@ -0,0 +1,148 @@
load common
begin_rsync() {
apt-get -qq install debootstrap rsync
if [ ! -d "$BN_SRCDIR" ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR"
fi
}
setup_rsync() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.rsync"
when = manual
[general]
log = ${BATS_TMPDIR}/log/rsync.log
mountpoint = $BN_BACKUPDIR
backupdir = testrsync
format =
[source]
from = local
include = $BN_SRCDIR
exclude = var
[dest]
dest = local
host =
user =
id_file = /root/.ssh/id_ed25519
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.rsync"
}
finish_rsync() {
cleanup_backups local remote
}
@test "create local backup, short format" {
cleanup_backups local
setconfig general format short
mkdir -p "${BN_BACKUPDIR}/testrsync"
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify local backup, short format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/bntest.0/"
[ "$status" -eq 0 ]
[ "$output" == ".d..t...... ./" ]
}
@test "verify local backup rotation, short format" {
skip "not implemented"
}
@test "create local backup, long format" {
cleanup_backups local
setconfig general format long
mkdir -p "${BN_BACKUPDIR}/testrsync"
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify local backup, long format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/daily.1/"
[ "$status" -eq 0 ]
[ "$output" == ".d..t...... ./" ]
}
@test "verify local backup rotation, long format" {
skip "not implemented"
}
@test "create local backup, mirror format" {
cleanup_backups local
setconfig general format mirror
mkdir -p "${BN_BACKUPDIR}/testrsync"
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify local backup, mirror format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/"
[ "$status" -eq 0 ]
[ "$output" == ".d..t...... ./" ]
}
@test "create remote backup, short format" {
cleanup_backups remote
setconfig general format short
setconfig dest dest remote
setconfig dest host "$BN_REMOTEHOST"
setconfig dest user "$BN_REMOTEUSER"
remote_command "mkdir -p \"${BN_BACKUPDIR}/testrsync\""
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify remote backup, short format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_REMOTEUSER}@${BN_REMOTEHOST}:${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/bntest.0"
[ "$status" -eq 0 ]
! echo "$output" | grep -qv '^skipping non-regular file'
}
@test "verify remote backup rotation, short format" {
skip "not implemented"
}
@test "create remote backup, long format" {
cleanup_backups remote
setconfig general format long
setconfig dest dest remote
setconfig dest host "$BN_REMOTEHOST"
setconfig dest user "$BN_REMOTEUSER"
remote_command "mkdir -p \"${BN_BACKUPDIR}/testrsync\""
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify remote backup, long format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_REMOTEUSER}@${BN_REMOTEHOST}:${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/daily.1"
[ "$status" -eq 0 ]
! echo "$output" | grep -qv '^skipping non-regular file'
}
@test "verify remote backup rotation, long format" {
skip "not implemented"
}
@test "create remote backup, mirror format" {
cleanup_backups remote
setconfig general format mirror
setconfig dest dest remote
setconfig dest host "$BN_REMOTEHOST"
setconfig dest user "$BN_REMOTEUSER"
remote_command "mkdir -p \"${BN_BACKUPDIR}/testrsync\""
runaction
greplog "Debug: Rsync transfer of $BN_SRCDIR finished successfully.$"
}
@test "verify remote backup, mirror format" {
run rsync -ain --exclude var --delete "${BN_SRCDIR}/" "${BN_REMOTEUSER}@${BN_REMOTEHOST}:${BN_BACKUPDIR}/testrsync${BN_SRCDIR}/"
[ "$status" -eq 0 ]
! echo "$output" | grep -qv '^skipping non-regular file'
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

27
test/sh.bats Normal file
View File

@ -0,0 +1,27 @@
load common
setup_sh() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.sh"
!#/bin/sh
when = manual
touch "${BN_BACKUPDIR}/testsh"
EOF
chmod 0750 "${BATS_TMPDIR}/backup.d/test.sh"
}
teardown_sh() {
cleanup_backups local
}
@test "runs and creates file" {
runaction
[ -f "${BN_BACKUPDIR}/testsh" ]
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
}
@test "is not executed in test mode" {
testaction
[ ! -f "${BN_BACKUPDIR}/testsh" ]
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
}

92
test/sys.bats Normal file
View File

@ -0,0 +1,92 @@
load common
begin_sys() {
apt-get -qq install debconf-utils hwinfo lvm2 cryptsetup-bin parted
cat << EOF > "${BATS_TMPDIR}/backup.d/test.sys"
when = manual
parentdir = $BN_BACKUPDIR
packages = yes
partitions = yes
hardware = yes
luksheaders = yes
lvm = yes
mbr = yes
bios = no
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.sys"
# Setup LVM
pvcreate /dev/sdc
vgcreate vgtest /dev/sdc
lvcreate -L 12M -n lvtest vgtest /dev/sdc
# Setup LUKS
parted -s /dev/sdd mklabel msdos mkpart p 1MiB 50% mkpart p 50% 100%
partprobe
cryptsetup -q --type luks1 luksFormat /dev/sdd1 <<< 123test
cryptsetup -q --type luks2 luksFormat /dev/sdd2 <<< 123test
cryptsetup -q --type luks2 luksFormat /dev/sde <<< 123test
}
finish_sys() {
# remove test artifacts
rm -rf ${BN_BACKUPDIR}/*
# cleanup lvm
lvremove -f vgtest/lvtest
vgremove vgtest
pvremove /dev/sdc
# cleanup luks data and partition tables
dd if=/dev/zero of=/dev/sdc bs=512 count=1 conv=notrunc
dd if=/dev/zero of=/dev/sdd1 bs=512 count=2048 conv=notrunc
dd if=/dev/zero of=/dev/sdd2 bs=512 count=2048 conv=notrunc
dd if=/dev/zero of=/dev/sdd bs=512 count=1 conv=notrunc
dd if=/dev/zero of=/dev/sde bs=512 count=2048 conv=notrunc
}
@test "action runs without errors" {
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
}
@test "system report is created" {
[ -s "${BN_BACKUPDIR}/sysreport.txt" ]
}
@test "packages backup is made" {
[ -s "${BN_BACKUPDIR}/dpkg-selections.txt" ]
[ -s "${BN_BACKUPDIR}/debconfsel.txt" ]
}
@test "partitions backup is made" {
[ -s "${BN_BACKUPDIR}/partitions.sda.txt" ]
[ -s "${BN_BACKUPDIR}/partitions.sdd.txt" ]
}
@test "mbr backup is made" {
[ -s "${BN_BACKUPDIR}/mbr.sda.bin" ]
file "${BN_BACKUPDIR}/mbr.sda.bin" | grep -q "DOS/MBR boot sector"
}
@test "hardware info backup is made" {
[ -s "${BN_BACKUPDIR}/hardware.txt" ]
}
@test "lvm backup is made" {
[ -d "${BN_BACKUPDIR}/lvm" ]
[ -s "${BN_BACKUPDIR}/lvm/vgtest" ]
grep -q 'contents = "Text Format Volume Group"' "${BN_BACKUPDIR}/lvm/vgtest"
}
@test "luksheaders v1 partition backup is made" {
file "${BN_BACKUPDIR}/luksheader.sdd1.bin" | grep -q "LUKS encrypted file"
}
@test "luksheaders v2 partition backup is made" {
file "${BN_BACKUPDIR}/luksheader.sdd2.bin" | grep -q "LUKS encrypted file"
}
@test "luksheaders v2 device backup is made" {
file "${BN_BACKUPDIR}/luksheader.sde.bin" | grep -q "LUKS encrypted file"
}

87
test/tar.bats Normal file
View File

@ -0,0 +1,87 @@
load common
begin_tar() {
apt-get -qq install debootstrap ncompress zstd
if [ ! -d "$BN_SRCDIR" ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR"
fi
}
setup_tar() {
cat << EOF > "${BATS_TMPDIR}/backup.d/test.tar"
when = manual
backupname = bntest
backupdir = ${BN_BACKUPDIR}/tartest
compress = none
includes = $BN_SRCDIR
excludes = ${BN_SRCDIR}/var
EOF
chmod 0640 "${BATS_TMPDIR}/backup.d/test.tar"
}
teardown_tar() {
cleanup_backups local
}
@test "no compression" {
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tar)
[ -s "$archive" ]
tar xOf "$archive" &> /dev/null
}
@test "compress compression" {
setconfig compress compress
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tar.compress)
[ -s "$archive" ]
tar xZOf "$archive" &> /dev/null
}
@test "gzip compression" {
setconfig compress gzip
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tgz)
[ -s "$archive" ]
tar xzOf "$archive" &> /dev/null
}
@test "bzip2 compression" {
setconfig compress bzip
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tar.bz2)
[ -s "$archive" ]
tar xjOf "$archive" &> /dev/null
}
@test "xz compression" {
setconfig compress xz
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tar.xz)
[ -s "$archive" ]
tar xJOf "$archive" &> /dev/null
}
@test "zstd compression" {
setconfig compress zstd
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 0 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tar.zst)
[ -s "$archive" ]
tar --zstd -xOf "$archive" &> /dev/null
}
@test "unknown compression, defaults to gzip" {
setconfig compress foo
runaction
greplog "Info: FINISHED: 1 actions run. 0 fatal. 0 error. 1 warning."
archive=$(find "${BN_BACKUPDIR}/tartest" -maxdepth 1 -name bntest-\*.tgz)
[ -s "$archive" ]
tar xzOf "$archive" &> /dev/null
}

56
test/test.sh Executable file
View File

@ -0,0 +1,56 @@
#!/bin/bash
# A minimal testsuite for backupninja
# This is meant to be run inside a development environment,
# so give the user a chance to bail
if [ ! -d "/vagrant" ]; then
read -p "This doesn't look like a test environment (Vagrant). Continue anyway? " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
fi
# Are we root?
if [[ "$USER" != "root" ]]; then
echo "Please run the test suite as root."
exit 1
fi
# Is backupninja in $PATH ?
if [ ! "$(which backupninja)" ]; then
echo "Couldn't find 'backupninja', is it installed?"
exit 1
fi
# Install basic test dependencies
apt-get -qq install bats mailutils faketime crudini
# Create a temporary base directory
TMPDIR=$(mktemp -t -d bntest.XXXXXX)
export TMPDIR
# Mount temporary directories in tmpfs
# this should speed up the tests a little bit
mount -t tmpfs tmpfs "$TMPDIR"
mount -t tmpfs tmpfs /var/backups
# Run actual tests
if [ -z "$1" ]; then
for t in "$(dirname "$0")"/*.bats; do
echo "# $(basename -s .bats "$t")"
bats "$t"
echo
done
else
echo "# $1"
bats "$(dirname "$0")/${1}.bats"
fi
# Clean up
umount "$TMPDIR"
umount /var/backups
rmdir "$TMPDIR"
exit 0