#!/bin/sh
set -eu

# Cleanup of snapshots, added as trap so it's always executed even in case of a
# failure or ctrl+c.
cleanup () {
    btrfs subvolume delete "$snapshot"
}

usage () {
    echo "Usage: $(basename "$0") DESTINATION" >&2
    exit 1
}

get_mountpoint () {
    df --output=target "$1" | tail +2
}

get_snapshot () {
    name="$1"
    mountpoint="$(docker volume inspect --format '{{ .Mountpoint }}' "$name" )"
    snapshot="$(dirname "$mountpoint")/snapshot)"
    echo "$snapshot"
}

if [ "$#" -ne 1 ]
then
    usage
elif [ ! -d "$1" ]
then
    echo "Destinaton $1 not found or is not a directory." >&2
    exit 1
elif [ "$(id -u)" -ne 0 ]
then
    echo "This program must run as root." >&2
    exit 1
elif ! command -v btrfs >/dev/null
then
    echo 'btrfs-progs must be installed.' >&2
    exit 1
elif ! docker info --format '{{ .ServerVersion }}' >/dev/null
then
    echo 'Docker must be installed and running.' >&2
    exit 1
elif ! df --type=btrfs /var/lib/docker/volumes >/dev/null
then
    echo "Docker volumes are not on a btrfs filesystem." >&2
    exit 1
elif ! df --type=btrfs "$1" >/dev/null
then
    echo "Destination $1 is not on a btrfs filesystem." >&2
    exit 1
elif [ "$(df --output=target /var/lib/docker/volumes)" != "$(df --output=target "$1")" ]
then
    echo "The destination $1 is not on the same btrfs volume as the Docker volumes." >&2
    exit 1
elif [ -z "$(docker volume ls --filter label=snapshot=true --format '{{ .Name }}')" ]
then
    echo "No Docker volumes marked for backup, exiting." >&2
    exit 1
fi

dest="$1"
root_volume="$(get_mountpoint /var/lib/docker/volumes)"
snapshot="$(mktemp --dry-run "--tmpdir=$root_volume")"
trap 'cleanup' INT QUIT EXIT TERM
btrfs subvolume snapshot "$root_volume" "$snapshot"

for name in $(docker volume ls --filter label=snapshot=true --format '{{ .Name }}')
do
    echo "Backing up $name."
    mountpoint="$(docker volume inspect --format '{{ .Mountpoint }}' "$name")"
    src="$snapshot/${mountpoint#$root_volume/}"
    # shellcheck disable=SC2115
    [ ! -e "$dest/$name" ] || rm -rf "$dest/$name"
    cp --archive --force --reflink=always "$src" "$dest/$name"
done
