feat: backup cron
This commit is contained in:
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
umask 077
|
||||
|
||||
require_variable() {
|
||||
variable_name="$1"
|
||||
eval "variable_value=\${$variable_name:-}"
|
||||
|
||||
if [ -z "$variable_value" ]; then
|
||||
echo "$variable_name is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
aws_cli() {
|
||||
endpoint="${BACKUP_S3_ENDPOINT:-${AWS_ENDPOINT:-}}"
|
||||
|
||||
if [ -n "$endpoint" ]; then
|
||||
aws --endpoint-url "$endpoint" "$@"
|
||||
else
|
||||
aws "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
for variable in \
|
||||
AWS_ACCESS_KEY_ID \
|
||||
AWS_DEFAULT_REGION \
|
||||
AWS_SECRET_ACCESS_KEY \
|
||||
BACKUP_S3_BUCKET \
|
||||
DB_DATABASE \
|
||||
DB_HOST \
|
||||
DB_PASSWORD \
|
||||
DB_USERNAME
|
||||
do
|
||||
require_variable "$variable"
|
||||
done
|
||||
|
||||
backup_directory="${BACKUP_DIRECTORY:-/backups}"
|
||||
backup_prefix="${BACKUP_S3_PREFIX:-database-backups/postgres}"
|
||||
backup_prefix="${backup_prefix#/}"
|
||||
backup_prefix="${backup_prefix%/}"
|
||||
backup_label="${1:-manual}"
|
||||
backup_label="$(printf "%s" "$backup_label" | tr -cd "A-Za-z0-9_-")"
|
||||
retention_days="${BACKUP_LOCAL_RETENTION_DAYS:-7}"
|
||||
timestamp="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||
database_name="$(printf "%s" "$DB_DATABASE" | tr -cd "A-Za-z0-9_-")"
|
||||
backup_name="${timestamp}-${database_name}-${backup_label:-manual}.dump"
|
||||
backup_path="$backup_directory/$backup_name"
|
||||
temporary_path="$backup_path.partial"
|
||||
checksum_path="$backup_path.sha256"
|
||||
lock_directory="/tmp/bowli-database-backup.lock"
|
||||
|
||||
case "$retention_days" in
|
||||
""|*[!0-9]*)
|
||||
echo "BACKUP_LOCAL_RETENTION_DAYS must be a positive integer." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! mkdir "$lock_directory" 2>/dev/null; then
|
||||
echo "Another database backup is already running." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
rm -f "$temporary_path"
|
||||
rmdir "$lock_directory" 2>/dev/null || true
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
mkdir -p "$backup_directory"
|
||||
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
export PGCONNECT_TIMEOUT="${PGCONNECT_TIMEOUT:-10}"
|
||||
|
||||
echo "Creating PostgreSQL backup: $backup_name"
|
||||
pg_dump \
|
||||
--host="$DB_HOST" \
|
||||
--port="${DB_PORT:-5432}" \
|
||||
--username="$DB_USERNAME" \
|
||||
--dbname="$DB_DATABASE" \
|
||||
--format=custom \
|
||||
--compress=9 \
|
||||
--no-owner \
|
||||
--no-acl \
|
||||
--file="$temporary_path"
|
||||
|
||||
pg_restore --list "$temporary_path" >/dev/null
|
||||
mv "$temporary_path" "$backup_path"
|
||||
|
||||
(
|
||||
cd "$backup_directory"
|
||||
sha256sum "$backup_name" > "$backup_name.sha256"
|
||||
)
|
||||
|
||||
s3_directory="s3://$BACKUP_S3_BUCKET"
|
||||
if [ -n "$backup_prefix" ]; then
|
||||
s3_directory="$s3_directory/$backup_prefix"
|
||||
fi
|
||||
|
||||
echo "Uploading backup to $s3_directory/"
|
||||
aws_cli s3 cp \
|
||||
"$backup_path" \
|
||||
"$s3_directory/$backup_name" \
|
||||
--sse AES256 \
|
||||
--only-show-errors
|
||||
aws_cli s3 cp \
|
||||
"$checksum_path" \
|
||||
"$s3_directory/$backup_name.sha256" \
|
||||
--sse AES256 \
|
||||
--only-show-errors
|
||||
|
||||
touch "$backup_directory/.last-success"
|
||||
|
||||
find "$backup_directory" -type f \
|
||||
\( -name "*.dump" -o -name "*.dump.sha256" \) \
|
||||
-mtime "+$retention_days" \
|
||||
-delete
|
||||
|
||||
echo "Database backup completed: $backup_name"
|
||||
Reference in New Issue
Block a user