80 lines
2.5 KiB
Bash
80 lines
2.5 KiB
Bash
#!/bin/sh
|
|
|
|
# crond gives the job a bare environment (HOME/PATH/SHELL only), so the cron
|
|
# entry passes --from-cron and the env dumped by entrypoint.sh is restored here.
|
|
# A manual run keeps the environment it was invoked with.
|
|
if [ "${1:-}" = "--from-cron" ] && [ -f /tmp/container.env ]; then
|
|
. /tmp/container.env
|
|
fi
|
|
|
|
set -u
|
|
|
|
BACKUP_FILE="sqlite_$(date "+%F-%H%M%S").tar.gz"
|
|
DUMP=/tmp/db.sqlite
|
|
ARCHIVE="/tmp/${BACKUP_FILE}"
|
|
DEST="${MINIO_PATH%/}/"
|
|
|
|
log() { echo "[$(date '+%F %T')] $*"; }
|
|
|
|
# healthchecks.io ping. Self-hosted instance is supported via HEALTHCHECK_URL.
|
|
# Final url: ${HEALTHCHECK_URL}/${HEALTHCHECK_UUID}[/start|/fail]
|
|
hc_ping() {
|
|
[ -n "${HEALTHCHECK_URL:-}" ] || return 0
|
|
url="${HEALTHCHECK_URL%/}"
|
|
[ -n "${HEALTHCHECK_UUID:-}" ] && url="${url}/${HEALTHCHECK_UUID}"
|
|
[ -n "${1:-}" ] && url="${url}/$1"
|
|
curl -fsS -m 10 --retry 3 -o /dev/null "$url" \
|
|
|| log "WARN: healthcheck ping failed: ${url}"
|
|
}
|
|
|
|
cleanup() { rm -f "$DUMP" "$ARCHIVE"; }
|
|
|
|
fail() {
|
|
log "ERROR: $*"
|
|
cleanup
|
|
hc_ping fail
|
|
exit 1
|
|
}
|
|
|
|
[ -n "${DB_FILE:-}" ] || fail "DB_FILE is not set"
|
|
[ -f "$DB_FILE" ] || fail "database not found: $DB_FILE"
|
|
[ -n "${MINIO_PATH:-}" ] || fail "MINIO_PATH is not set"
|
|
|
|
hc_ping start
|
|
log "backup started: $DB_FILE -> ${DEST}${BACKUP_FILE}"
|
|
|
|
# 1. Consistent snapshot
|
|
rm -f "$DUMP"
|
|
sqlite3 "$DB_FILE" ".backup '$DUMP'" || fail "sqlite3 .backup failed"
|
|
[ -s "$DUMP" ] || fail "dump is empty"
|
|
|
|
# 2. Verify the snapshot before shipping it anywhere
|
|
INTEGRITY=$(sqlite3 "$DUMP" "PRAGMA integrity_check;" 2>&1) \
|
|
|| fail "integrity_check failed to run: $INTEGRITY"
|
|
[ "$INTEGRITY" = "ok" ] || fail "integrity_check: $INTEGRITY"
|
|
|
|
# 3. Pack
|
|
tar -C /tmp -zcf "$ARCHIVE" "$(basename "$DUMP")" || fail "tar failed"
|
|
LOCAL_SIZE=$(wc -c < "$ARCHIVE" | tr -d ' ')
|
|
[ "$LOCAL_SIZE" -gt 0 ] || fail "archive is empty"
|
|
|
|
# 4. Upload
|
|
/scripts/minio_uploader.sh copy "$ARCHIVE" "$DEST" || fail "upload failed"
|
|
|
|
# 5. Verify the remote object exists and matches the local size
|
|
REMOTE_SIZE=$(/scripts/minio_uploader.sh size "${DEST}${BACKUP_FILE}")
|
|
[ -n "$REMOTE_SIZE" ] || fail "uploaded file not found at ${DEST}${BACKUP_FILE}"
|
|
[ "$REMOTE_SIZE" = "$LOCAL_SIZE" ] \
|
|
|| fail "size mismatch: local=${LOCAL_SIZE} remote=${REMOTE_SIZE}"
|
|
|
|
log "uploaded and verified: ${DEST}${BACKUP_FILE} (${LOCAL_SIZE} bytes)"
|
|
cleanup
|
|
|
|
# 6. Rotate old backups
|
|
if [ -n "${DELETE_AFTER:-}" ] && [ "${DELETE_AFTER}" -gt 0 ] 2>/dev/null; then
|
|
/scripts/deleteold.sh || fail "rotation of old backups failed"
|
|
fi
|
|
|
|
log "backup finished successfully"
|
|
hc_ping
|