allow to select the archive to restore from

Signed-off-by: szaimen <szaimen@e.mail.de>
This commit is contained in:
szaimen 2021-12-07 19:10:05 +01:00
parent badd8d02a9
commit 63e0849215
8 changed files with 63 additions and 15 deletions

View file

@ -1,13 +1,9 @@
#!/bin/bash #!/bin/bash
# Variables
BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Functions # Functions
get_start_time(){ get_start_time(){
START_TIME=$(date +%s) START_TIME=$(date +%s)
CURRENT_DATE=$(date --date @"$START_TIME" +"%Y%m%d_%H%M%S") CURRENT_DATE=$(date --date @"$START_TIME" +"%Y%m%d_%H%M%S")
CURRENT_DATE_READABLE=$(date --date @"$START_TIME" +"%d.%m.%Y - %H:%M:%S")
} }
get_expiration_time() { get_expiration_time() {
END_TIME=$(date +%s) END_TIME=$(date +%s)
@ -150,9 +146,6 @@ if [ "$BORG_MODE" = backup ]; then
# Remove the update skip file because the backup was successful # Remove the update skip file because the backup was successful
rm -f "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update" rm -f "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update"
echo "$CURRENT_DATE,$CURRENT_DATE_READABLE" >> "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
# Prune options # Prune options
BORG_PRUNE_OPTS=(--stats --progress --keep-within=7d --keep-weekly=4 --keep-monthly=6 "$BORG_BACKUP_DIRECTORY") BORG_PRUNE_OPTS=(--stats --progress --keep-within=7d --keep-weekly=4 --keep-monthly=6 "$BORG_BACKUP_DIRECTORY")
@ -172,17 +165,20 @@ fi
# Do the restore # Do the restore
if [ "$BORG_MODE" = restore ]; then if [ "$BORG_MODE" = restore ]; then
get_start_time get_start_time
echo "Restoring the last backup..."
# Perform the restore # Perform the restore
FIRST_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)" if [ -n "$SELECTED_RESTORE_TIME" ]; then
SELECTED_ARCHIVE"$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | grep "$SELECTED_RESTORE_TIME" | awk -F " " '{print $1}' | head -1)"
else
SELECTED_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)"
fi
echo "Restoring '$SELECTED_ARCHIVE'..."
mkdir -p /tmp/borg mkdir -p /tmp/borg
if ! borg mount "$BORG_BACKUP_DIRECTORY::$FIRST_ARCHIVE" /tmp/borg; then if ! borg mount "$BORG_BACKUP_DIRECTORY::$SELECTED_ARCHIVE" /tmp/borg; then
echo "Could not mount the backup!" echo "Could not mount the backup!"
exit 1 exit 1
fi fi
if ! rsync --stats --archive --human-readable -vv --delete \ if ! rsync --stats --archive --human-readable -vv --delete \
--exclude "nextcloud_aio_mastercontainer/data/backup_archives.list" \
--exclude "nextcloud_aio_mastercontainer/session/"** \ --exclude "nextcloud_aio_mastercontainer/session/"** \
--exclude "nextcloud_aio_mastercontainer/certs/"** \ --exclude "nextcloud_aio_mastercontainer/certs/"** \
/tmp/borg/nextcloud_aio_volumes/ /nextcloud_aio_volumes; then /tmp/borg/nextcloud_aio_volumes/ /nextcloud_aio_volumes; then

View file

@ -1,5 +1,8 @@
#!/bin/bash #!/bin/bash
# Variables
export BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Validate BORG_PASSWORD # Validate BORG_PASSWORD
if [ -z "$BORG_PASSWORD" ]; then if [ -z "$BORG_PASSWORD" ]; then
echo "BORG_PASSWORD is not allowed to be empty." echo "BORG_PASSWORD is not allowed to be empty."
@ -24,6 +27,12 @@ fi
# Remove lockfile # Remove lockfile
rm -f "/nextcloud_aio_volumes/nextcloud_aio_database_dump/backup-is-running" rm -f "/nextcloud_aio_volumes/nextcloud_aio_database_dump/backup-is-running"
# Get a list of all available borg archives
set -x
borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1","$3,$4}' > "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
set +x
if [ -n "$FAILED" ]; then if [ -n "$FAILED" ]; then
if [ "$BORG_MODE" = backup ]; then if [ "$BORG_MODE" = backup ]; then
# Add file to Nextcloud container so that it skips any update the next time # Add file to Nextcloud container so that it skips any update the next time

View file

@ -194,7 +194,8 @@
"internalPorts": [], "internalPorts": [],
"environmentVariables": [ "environmentVariables": [
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%", "BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
"BORG_MODE=%BORGBACKUP_MODE%" "BORG_MODE=%BORGBACKUP_MODE%",
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%"
], ],
"volumes": [ "volumes": [
{ {

View file

@ -87,6 +87,7 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(), 'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(),
'has_update_available' => $dockerActionManger->isAnyUpdateAvailable(), 'has_update_available' => $dockerActionManger->isAnyUpdateAvailable(),
'last_backup_time' => $configurationManager->GetLastBackupTime(), 'last_backup_time' => $configurationManager->GetLastBackupTime(),
'backup_times' => $configurationManager->GetBackupTimes(),
]); ]);
})->setName('profile'); })->setName('profile');
$app->get('/login', function ($request, $response, $args) use ($container) { $app->get('/login', function ($request, $response, $args) use ($container) {

View file

@ -83,6 +83,7 @@ class DockerController
public function StartBackupContainerRestore(Request $request, Response $response, $args) : Response { public function StartBackupContainerRestore(Request $request, Response $response, $args) : Response {
$config = $this->configurationManager->GetConfig(); $config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'restore'; $config['backup-mode'] = 'restore';
$config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'];
$this->configurationManager->WriteConfig($config); $this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER; $id = self::TOP_CONTAINER;

View file

@ -83,6 +83,30 @@ class ConfigurationManager
return $lastBackupTime; return $lastBackupTime;
} }
public function GetBackupTimes() : array {
if (!file_exists(DataConst::GetBackupArchivesList())) {
return $array[] = '';
}
$content = file_get_contents(DataConst::GetBackupArchivesList());
if ($content === "") {
return $array[] = '';
}
$backupLines = explode("\n", $content);
$backupTimes = array();
foreach($backupLines as $lines) {
$backupTimesTemp = explode(",", $lines);
$backupTimes[] = $backupTimesTemp[1];
}
if (!is_array($backupTimes)) {
return $array[] = '';
}
return $backupTimes;
}
public function wasStartButtonClicked() : bool { public function wasStartButtonClicked() : bool {
if (isset($this->GetConfig()['wasStartButtonClicked'])) { if (isset($this->GetConfig()['wasStartButtonClicked'])) {
return true; return true;
@ -152,6 +176,15 @@ class ConfigurationManager
return $config['backup-mode']; return $config['backup-mode'];
} }
public function GetSelectedRestoreTime() : string {
$config = $this->GetConfig();
if(!isset($config['selected-restore-time'])) {
$config['selected-restore-time'] = '';
}
return $config['selected-restore-time'];
}
public function GetAIOURL() : string { public function GetAIOURL() : string {
$config = $this->GetConfig(); $config = $this->GetConfig();
if(!isset($config['AIO_URL'])) { if(!isset($config['AIO_URL'])) {

View file

@ -212,6 +212,8 @@ class DockerActionManager
$replacements[1] = $this->configurationManager->GetBackupMode(); $replacements[1] = $this->configurationManager->GetBackupMode();
} elseif ($out[1] === 'AIO_URL') { } elseif ($out[1] === 'AIO_URL') {
$replacements[1] = $this->configurationManager->GetAIOURL(); $replacements[1] = $this->configurationManager->GetAIOURL();
} elseif ($out[1] === 'SELECTED_RESTORE_TIME') {
$replacements[1] = $this->configurationManager->GetSelectedRestoreTime();
} else { } else {
$replacements[1] = $this->configurationManager->GetSecret($out[1]); $replacements[1] = $this->configurationManager->GetSecret($out[1]);
} }

View file

@ -206,11 +206,16 @@
<input class="button" type="submit" value="Check backup integrity" onclick="return confirm('Check backup integrity? Are you sure that you want to check the backup? This can take a long time depending on the size of your backup.')" /><br/> <input class="button" type="submit" value="Check backup integrity" onclick="return confirm('Check backup integrity? Are you sure that you want to check the backup? This can take a long time depending on the size of your backup.')" /><br/>
</form> </form>
Click on the button below to restore the last backup from {{ last_backup_time }}. This will overwrite all your files with the state of the backup. It makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br> Choose the backup that you want to restore and click on the button below to restore the selected backup. This will overwrite all your files with the state of the backup so you should consider creating a backup first. It also makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
<form method="POST" action="/api/docker/restore" class="xhr"> <form method="POST" action="/api/docker/restore" class="xhr" id="restore_selection">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}"> <input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}"> <input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input class="button" type="submit" value="Restore last backup" onclick="return confirm('Restore last backup? Are you sure that you want to restore the last backup? This will stop all running containers and restore the last backup from {{ last_backup_time }}. You might want to check the backup integrity first.')" /> <select id="selected_restore_time" name="selected_restore_time" form="restore_selection">
{% for restore_time in backup_times %}
<option value="{{ restore_time }}">{{ restore_time }}"</option>
{% endfor %}
</select>
<input class="button" type="submit" value="Restore selected backup" onclick="return confirm('Restore the selected backup? Are you sure that you want to restore the selected backup? This will stop all running containers and restore the selected backup. It is recommended to create a backup first. You might also want to check the backup integrity.')" />
</form> </form>
{% endif %} {% endif %}
{% endif %} {% endif %}