Skip to content

Fix for Sentry #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ RUN apk update \
postgresql14-client \
py3-magic \
py3-dateutil \
s3cmd
s3cmd \
curl \
jq

# Install sentry-cli
RUN curl -sL https://sentry.io/get-cli/ | bash

COPY application/ /data/
WORKDIR /data
Expand Down
147 changes: 85 additions & 62 deletions application/backup.sh
Original file line number Diff line number Diff line change
@@ -1,90 +1,113 @@
#!/usr/bin/env bash

# Function to send error to Sentry
send_error_to_sentry() {
local error_message="$1"
local db_name="$2"
local status_code="$3"

if [ -n "${SENTRY_DSN}" ]; then
wget -q --header="Content-Type: application/json" \
--post-data="{
\"message\": \"${error_message}\",
\"level\": \"error\",
\"extra\": {
\"database\": \"${db_name}\",
\"status_code\": \"${status_code}\",
\"hostname\": \"$(hostname)\"
}
}" \
-O - "${SENTRY_DSN}"
fi
# Initialize logging with timestamp
log() {
local level="$1";
local message="$2";
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${level}: ${message}";
}

MYNAME="postgresql-backup-restore"
STATUS=0
# Sentry reporting with validation and backwards compatibility
error_to_sentry() {
local error_message="$1";
local db_name="$2";
local status_code="$3";

echo "${MYNAME}: backup: Started"
# Check if SENTRY_DSN is configured - ensures backup continues
if [ -z "${SENTRY_DSN:-}" ]; then
log "DEBUG" "Sentry logging skipped - SENTRY_DSN not configured";
return 0;
fi

echo "${MYNAME}: Backing up ${DB_NAME}"
# Validate SENTRY_DSN format
if ! [[ "${SENTRY_DSN}" =~ ^https://[^@]+@[^/]+/[0-9]+$ ]]; then
log "WARN" "Invalid SENTRY_DSN format - Sentry logging will be skipped";
return 0;
fi

start=$(date +%s)
$(PGPASSWORD=${DB_USERPASSWORD} pg_dump --host=${DB_HOST} --username=${DB_USER} --create --clean ${DB_OPTIONS} --dbname=${DB_NAME} > /tmp/${DB_NAME}.sql) || STATUS=$?
end=$(date +%s)
# Attempt to send event to Sentry
if sentry-cli send-event \
--message "${error_message}" \
--level error \
--tag "database:${db_name}" \
--tag "status:${status_code}"; then
log "DEBUG" "Successfully sent error to Sentry - Message: ${error_message}, Database: ${db_name}, Status: ${status_code}";
else
log "WARN" "Failed to send error to Sentry, but continuing backup process";
fi

return 0;
}

MYNAME="postgresql-backup-restore";
STATUS=0;

log "INFO" "${MYNAME}: backup: Started";
log "INFO" "${MYNAME}: Backing up ${DB_NAME}";

start=$(date +%s);
$(PGPASSWORD=${DB_USERPASSWORD} pg_dump --host=${DB_HOST} --username=${DB_USER} --create --clean ${DB_OPTIONS} --dbname=${DB_NAME} > /tmp/${DB_NAME}.sql) || STATUS=$?;
end=$(date +%s);

if [ $STATUS -ne 0 ]; then
error_message="${MYNAME}: FATAL: Backup of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds."
echo "${error_message}"
send_error_to_sentry "${error_message}" "${STATUS}" "${DB_NAME}"
exit $STATUS
error_message="${MYNAME}: FATAL: Backup of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds.";
log "ERROR" "${error_message}";
error_to_sentry "${error_message}" "${DB_NAME}" "${STATUS}";
exit $STATUS;
else
echo "${MYNAME}: Backup of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds, ($(stat -c %s /tmp/${DB_NAME}.sql) bytes)."
log "INFO" "${MYNAME}: Backup of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds, ($(stat -c %s /tmp/${DB_NAME}.sql) bytes).";
fi

start=$(date +%s)
gzip -f /tmp/${DB_NAME}.sql || STATUS=$?
end=$(date +%s)
# Compression
start=$(date +%s);
gzip -f /tmp/${DB_NAME}.sql || STATUS=$?;
end=$(date +%s);

if [ $STATUS -ne 0 ]; then
error_message="${MYNAME}: FATAL: Compressing backup of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds."
echo "${error_message}"
send_error_to_sentry "${error_message}" "${STATUS}" "${DB_NAME}"
exit $STATUS
error_message="${MYNAME}: FATAL: Compressing backup of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds.";
log "ERROR" "${error_message}";
error_to_sentry "${error_message}" "${DB_NAME}" "${STATUS}";
exit $STATUS;
else
echo "${MYNAME}: Compressing backup of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds."
log "INFO" "${MYNAME}: Compressing backup of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds.";
fi

start=$(date +%s)
s3cmd put /tmp/${DB_NAME}.sql.gz ${S3_BUCKET} || STATUS=$?
end=$(date +%s)

# S3 Upload
start=$(date +%s);
s3cmd put /tmp/${DB_NAME}.sql.gz ${S3_BUCKET} || STATUS=$?;
end=$(date +%s);
if [ $STATUS -ne 0 ]; then
error_message="${MYNAME}: FATAL: Copy backup to ${S3_BUCKET} of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds."
echo "${error_message}"
send_error_to_sentry "${error_message}" "${STATUS}" "${DB_NAME}"
exit $STATUS
error_message="${MYNAME}: FATAL: Copy backup to ${S3_BUCKET} of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds.";
log "ERROR" "${error_message}";
error_to_sentry "${error_message}" "${DB_NAME}" "${STATUS}";
exit $STATUS;
else
echo "${MYNAME}: Copy backup to ${S3_BUCKET} of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds."
log "INFO" "${MYNAME}: Copy backup to ${S3_BUCKET} of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds.";
fi

# Backblaze B2 Upload
if [ "${B2_BUCKET}" != "" ]; then
start=$(date +%s)
start=$(date +%s);
s3cmd \
--access_key=${B2_APPLICATION_KEY_ID} \
--secret_key=${B2_APPLICATION_KEY} \
--host=${B2_HOST} \
--host-bucket='%(bucket)s.'"${B2_HOST}" \
put /tmp/${DB_NAME}.sql.gz s3://${B2_BUCKET}/${DB_NAME}.sql.gz
STATUS=$?
end=$(date +%s)
--access_key=${B2_APPLICATION_KEY_ID} \
--secret_key=${B2_APPLICATION_KEY} \
--host=${B2_HOST} \
--host-bucket='%(bucket)s.'"${B2_HOST}" \
put /tmp/${DB_NAME}.sql.gz s3://${B2_BUCKET}/${DB_NAME}.sql.gz;
STATUS=$?;
end=$(date +%s);
if [ $STATUS -ne 0 ]; then
error_message="${MYNAME}: FATAL: Copy backup to Backblaze B2 bucket ${B2_BUCKET} of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds."
echo "${error_message}"
send_error_to_sentry "${error_message}" "${STATUS}"
exit $STATUS
error_message="${MYNAME}: FATAL: Copy backup to Backblaze B2 bucket ${B2_BUCKET} of ${DB_NAME} returned non-zero status ($STATUS) in $(expr ${end} - ${start}) seconds.";
log "ERROR" "${error_message}";
error_to_sentry "${error_message}" "${DB_NAME}" "${STATUS}";
exit $STATUS;
else
echo "${MYNAME}: Copy backup to Backblaze B2 bucket ${B2_BUCKET} of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds."
log "INFO" "${MYNAME}: Copy backup to Backblaze B2 bucket ${B2_BUCKET} of ${DB_NAME} completed in $(expr ${end} - ${start}) seconds.";
fi
fi

echo "${MYNAME}: backup: Completed"
echo "postgresql-backup-restore: backup: Completed";

log "INFO" "${MYNAME}: backup: Completed";

exit $STATUS;