8361ab701c
This adds a retirement and purge list to the borg management role. The idea here is that when a backed-up host is shut-down, we add its backup user to the retired list. On the next ansible run the user will be disabled on the backup-server and the backup repo marked as retired. On the next prune, we will trim the backup to only the last run to save space. This gives us a grace period to restore if we should need to. When we are sure we don't want the data, we can put it in the purge list, and the backup repo is removed on the next ansible run (hosts can go straight into this if we want). This allows us to have a review process/history before we purge data. To test, we create a fake "borg-retired" user on the backup-server, and give it a simple backup. This is marked as retired, which is reflected in the testinfra run of the prune script. Similarly a "borg-purge" user is created, and we ensure it's backup dir is removed. Documentation is updated. Change-Id: I5dff0a9d35b11a1f021048a12ecddce952c0c13c
130 lines
4.6 KiB
Python
130 lines
4.6 KiB
Python
# Copyright 2019 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import os.path
|
|
import pytest
|
|
|
|
testinfra_hosts = ['borg-backup01.region.provider.opendev.org',
|
|
'borg-backup-bionic.opendev.org',
|
|
'borg-backup-focal.opendev.org',
|
|
'borg-backup-jammy.opendev.org']
|
|
|
|
|
|
def test_borg_installed(host):
|
|
f = host.file('/opt/borg/bin/borg')
|
|
assert f.exists
|
|
|
|
cmd = host.run('/opt/borg/bin/borg --version')
|
|
assert cmd.succeeded
|
|
# NOTE(ianw): deliberately pinned; we want to be careful if we
|
|
# update that the new version is compatible with old repos.
|
|
assert '1.1.18' in cmd.stdout
|
|
|
|
def test_borg_server_users(host):
|
|
hostname = host.backend.get_hostname()
|
|
if hostname != 'borg-backup01.region.provider.opendev.org':
|
|
pytest.skip()
|
|
|
|
for username in ('borg-borg-backup-bionic',
|
|
'borg-borg-backup-focal',
|
|
'borg-borg-backup-jammy'):
|
|
homedir = os.path.join('/opt/backups/', username)
|
|
borg_repo = os.path.join(homedir, 'backup')
|
|
authorized_keys = os.path.join(homedir, '.ssh', 'authorized_keys')
|
|
|
|
user = host.user(username)
|
|
assert user.exists
|
|
assert user.home == homedir
|
|
|
|
f = host.file(authorized_keys)
|
|
assert f.exists
|
|
assert f.contains("ssh-ed25519")
|
|
|
|
f = host.file(borg_repo)
|
|
assert f.exists
|
|
|
|
# test retired stamp is made for host in retired group
|
|
f = host.file('/opt/backups/borg-retired/.retired')
|
|
assert f.exists
|
|
|
|
# test purge for host in purge group
|
|
f = host.file('/opt/backups/borg-purge/backup')
|
|
assert not f.exists
|
|
|
|
def test_borg_backup_host_config(host):
|
|
hostname = host.backend.get_hostname()
|
|
if hostname == 'borg-backup01.region.provider.opendev.org':
|
|
pytest.skip()
|
|
|
|
f = host.file('/usr/local/bin/borg-backup')
|
|
assert f.exists
|
|
|
|
f = host.file('/root/.ssh/id_borg_backup_ed25519')
|
|
assert f.exists
|
|
|
|
f = host.file('/root/.ssh/config')
|
|
assert f.exists
|
|
assert f.contains('Host borg-backup01.region.provider.opendev.org')
|
|
|
|
def test_borg_backup(host):
|
|
hostname = host.backend.get_hostname()
|
|
if hostname == 'borg-backup01.region.provider.opendev.org':
|
|
pytest.skip()
|
|
|
|
cmd = host.run(
|
|
'/usr/local/bin/borg-backup borg-backup01.region.provider.opendev.org 2>> '
|
|
'/var/log/borg-backup-borg-backup01.region.provider.opendev.org.log')
|
|
assert cmd.succeeded
|
|
|
|
cmd = host.run(
|
|
'/usr/local/bin/borg-mount borg-backup01.region.provider.opendev.org')
|
|
assert cmd.succeeded
|
|
|
|
cmd = host.run('ls /opt/backups')
|
|
# this directory should now have a directory
|
|
# borg-backup-<distro>-YYYY-MM-DDT...
|
|
assert hostname.split('.')[0] in cmd.stdout
|
|
|
|
# unmount it for sanity
|
|
cmd = host.run('umount /opt/backups')
|
|
assert cmd.succeeded
|
|
|
|
def test_borg_server_prune(host):
|
|
hostname = host.backend.get_hostname()
|
|
if hostname != 'borg-backup01.region.provider.opendev.org':
|
|
pytest.skip()
|
|
|
|
# bit of a hack; instead of making a host, backing it up, and then
|
|
# retiring it -- which would require testing multiple runs of the
|
|
# backup process -- simulate the retired user being active by just
|
|
# making a small archive. This ensure the prune script works on
|
|
# user flagged as retired.
|
|
cmd = host.run('dd if=/dev/urandom of=/tmp/borg-backup.random bs=1M count=5')
|
|
assert cmd.succeeded
|
|
cmd = host.run('sudo -u borg-retired /opt/borg/bin/borg init --encryption=none /opt/backups/borg-retired/backup')
|
|
assert cmd.succeeded
|
|
cmd = host.run('sudo -u borg-retired /opt/borg/bin/borg create /opt/backups/borg-retired/backup::test-9999-12-12T00:00:00 /tmp/borg-backup.random')
|
|
assert cmd.succeeded
|
|
|
|
cmd = host.run('echo "prune" | NO_LOG_FILE=1 /usr/local/bin/prune-borg-backups &> /var/log/prune-borg-backups.log')
|
|
assert cmd.succeeded
|
|
|
|
def test_borg_server_verify(host):
|
|
hostname = host.backend.get_hostname()
|
|
if hostname != 'borg-backup01.region.provider.opendev.org':
|
|
pytest.skip()
|
|
|
|
cmd = host.run('/usr/local/bin/verify-borg-backups &> /var/log/verify-borg-backups.log')
|
|
assert cmd.succeeded
|