From edfe644496189da09c12009d8c670e5100515dd9 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Thu, 26 May 2022 21:11:20 +0200 Subject: Add a first iteration of a backup subcommand --- lib/subcommands/backup.bash | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 lib/subcommands/backup.bash (limited to 'lib') diff --git a/lib/subcommands/backup.bash b/lib/subcommands/backup.bash new file mode 100644 index 0000000..2765172 --- /dev/null +++ b/lib/subcommands/backup.bash @@ -0,0 +1,155 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2022 Patrick Spek +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +subcommand() { + # Retrieve the passphrase used by Borg + BORG_PASSPHRASE="$(config "bashtard.backup.key")" + + if [[ -z "$BORG_PASSPHRASE" ]] + then + emerg "$BASHTARD_NAME/backup" "No backup key configured" + return 3 + fi + + export BORG_PASSPHRASE + + # Run backups for all configured elements, if none were explicitly + # specified + if (( $# < 1 )) + then + debug "$BASHTARD_NAME/backup" "Retrieving elements from config" + + while read -r element + do + set -- "$@" "$(config "$element")" + done < <(config_subkeys "bashtard.backup.elements") + fi + + # If there are still no configured elements, this is a good time to + # return an error instead + if (( $# < 1 )) + then + emerg "$BASHTARD_NAME/backup" "No elements to back up" + return 3 + fi + + # Loop over all repositories, and run backup functions for each of them + while read -r key + do + index="$(awk -F. '{ print $NF }' <<< "$key")" + + for element in "$@" + do + "backup_$element" "$index" + done + + info "$BASHTARD_NAME/backup/$index" "Backups completed" + done < <(config_subkeys "bashtard.backup.repositories") +} + +backup_filesystem() { + local index=$1 ; shift + local borg + local cmd_create + local cmd_prune + local indexes + + borg="$(config "bashtard.backup.borg.command" "borg")" + remote="$(config "bashtard.backups.borg.remote_paths.$index" "borg")" + repo="$(config "bashtard.backup.repositories.$index")" + + if ! command -v "$borg" > /dev/null 2>&1 + then + emerg "$BASHTARD_NAME/backup/$index" "The backup command depends on '$borg' being installed and available in your \$PATH" + return 4 + fi + + # Prune old backups + info "$BASHTARD_NAME/backup/$index" "Pruning filesystem backups in $repo" + cmd_prune=( + "$borg" "prune" + "--keep-daily" "$(config "bashtard.backup.keep.daily" 7)" + "--keep-weekly" "$(config "bashtard.backup.keep.weekly" 4)" + "--keep-monthly" "$(config "bashtard.backup.keep.monthly" 6)" + "--keep-yearly" "$(config "bashtard.backup.keep.yearly" 1)" + "--prefix" "{fqdn}-" + "--remote-path" "$remote" + "$repo/hostfs" + ) + + notice "$BASHTARD_NAME/backup/$index" "> ${cmd_prune[*]}" + ${cmd_prune[@]} + + # Create new backups + info "$BASHTARD_NAME/backup/$index" "Writing new filesystem backup to $repo" + cmd_create=( + "$borg" "create" + "--one-file-system" + "--remote-path" "$remote" + "$repo/hostfs::{fqdn}-$(datetime)" + ) + + # Add all paths to the command + while read -r path + do + cmd_create+=("$(config "$path")") + done < <(config_subkeys "bashtard.backup.fs.paths") + + notice "$BASHTARD_NAME/backup/$index" "> ${cmd_create[*]}" + ${cmd_create[@]} +} + +backup_database_postgresql() { + local index=$1 ; shift + local borg + local remote + local repo + + if ! command -v "psql" > /dev/null 2>&1 + then + emerg "$BASHTARD_NAME/backup/$index" "The backup command depends on 'psql' being installed and available in your \$PATH" + return 4 + fi + + if ! command -v "pg_dump" > /dev/null 2>&1 + then + emerg "$BASHTARD_NAME/backup/$index" "The backup command depends on 'pg_dump' being installed and available in your \$PATH" + return 4 + fi + + PGPASSWORD="$(config "bashtard.backup.db.postgresql.password" "")" + PGUSER="$(config "bashtard.backup.db.postgresql.user" "postgres")" + borg="$(config "bashtard.backup.borg.command" "borg")" + remote="$(config "bashtard.backups.borg.remote_paths.$index" "borg")" + repo="$(config "bashtard.backup.repositories.$index")" + + [[ $PGPASSWORD == "" ]] && export PGPASSWORD + export PGUSER + + while read -r database + do + [[ $database == "postgres" ]] && continue + [[ $database =~ template* ]] && continue + + # Prune old backups + info "$BASHTARD_NAME/backup/$index" "Pruning PostgreSQL backups of $database in $repo" + $borg prune \ + --keep-daily "$(config "bashtard.backup.keep.daily" 7)" \ + --keep-weekly "$(config "bashtard.backup.keep.weekly" 4)" \ + --keep-monthly "$(config "bashtard.backup.keep.monthly" 6)" \ + --keep-yearly "$(config "bashtard.backup.keep.yearly" 1)" \ + --prefix "$database-" \ + --remote-path "$remote" \ + "$repo/postgresql-$database" + + # Create new backups + info "$BASHTARD_NAME/backup/$index" "Writing new PostgreSQL backup of $database to $repo" + pg_dump "$database" \ + | borg create \ + --remote-path "$remote" \ + "$repo/postgresql-$database::$database-$(datetime)" + done < <(psql -AXt -d template1 -c "SELECT datname FROM pg_database") +} -- cgit v1.1