#!/usr/bin/env bash
# /usr/local/bin/pg-backup
# Dump to a private temp file, prove the file reads to the end, upload it, compare the
# upload with the local file. Any failure exits non-zero before anything is pruned.
set -euo pipefail

# Change these.
pg_filters=(--filter label=com.docker.compose.project=shop --filter label=com.docker.compose.service=postgres)
pg_user=shop
pg_db=shop
remote=offsite:shop-backups/postgres
min_bytes=50000000 # about half the size of a real dump of this database
keep=              # empty when a bucket lifecycle rule expires objects, else 30d. Always give a unit.

export RCLONE_CONFIG=/etc/pg-backup/rclone.conf

mapfile -t ids < <(docker ps -q --filter status=running "${pg_filters[@]}")
if [ "${#ids[@]}" -ne 1 ]; then
  echo "expected one running database container, found ${#ids[@]}" >&2
  exit 1
fi
pg=${ids[0]}

# Stop before dumping when the storage is unreachable or the disk could fill.
# A dump that fills the disk Postgres writes to can crash Postgres.
biggest=$(rclone lsjson --files-only "$remote" | jq 'map(.Size) | max // 0')
need=$(( (biggest > min_bytes * 2 ? biggest : min_bytes * 2) * 2 ))
free=$(df --output=avail -B1 /var/tmp | tail -n 1)
if [ "$free" -lt "$need" ]; then
  echo "$free bytes free under /var/tmp, want $need" >&2
  exit 1
fi

name="$pg_db-$(date -u +%Y%m%dT%H%M%SZ).dump"
tmp=$(mktemp -d -p /var/tmp pg-backup.XXXXXX)
trap 'rm -rf "$tmp"' EXIT

# No -t on docker exec: a terminal turns every LF byte into CR LF and wrecks the archive.
docker exec "$pg" pg_dump -Fc -U "$pg_user" -d "$pg_db" > "$tmp/$name"

size=$(stat -c %s "$tmp/$name")
if [ "$size" -lt "$min_bytes" ]; then
  echo "dump is $size bytes, below the floor of $min_bytes" >&2
  exit 1
fi

# Reads and decompresses every data block. pg_restore --list would pass a truncated file.
docker exec -i "$pg" pg_restore -f /dev/null < "$tmp/$name"

rclone copyto "$tmp/$name" "$remote/$name"
rclone check --download "$tmp" "$remote" --one-way --include "$name"

if [ -n "$keep" ]; then
  rclone delete --min-age "$keep" --use-server-modtime --include '*.dump' "$remote"
fi

echo "backup ok: $name, $size bytes"
