From bb060e5eec38b1f308facbc0fc9afab0a57bbd94 Mon Sep 17 00:00:00 2001 From: redxef Date: Tue, 30 Aug 2022 22:26:52 +0200 Subject: [PATCH] Add restore command and test. --- dvbackup.sh | 65 ++++++++++++++++++++++++++++++--------------- test/mock-docker.sh | 24 +++++++++++++++++ test/test.sh | 23 ++++++++++++++++ 3 files changed, 90 insertions(+), 22 deletions(-) create mode 100755 test/mock-docker.sh create mode 100755 test/test.sh diff --git a/dvbackup.sh b/dvbackup.sh index c42af0f..a796b16 100755 --- a/dvbackup.sh +++ b/dvbackup.sh @@ -1,50 +1,71 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +if [ -z "$DOCKER" ]; then + DOCKER=docker +fi + + +echo_and_run() { + echo "$@" + "$@" + return $? +} backup() { - local volume - local target - local target_dir - local target_name volume="$1" target="$(realpath "$2")" target_dir="$(dirname "$target")" target_name="$(basename "$target")" - test -z "$(docker volume ls | grep "$volume")" && { echo "Error: No such volume, aborting" >&2; exit 1; } + test -z "$("$DOCKER" volume ls | grep "$volume")" && { echo "Error: No such volume, aborting" >&2; exit 1; } test -z "$target_dir" && { echo "Error: No base folder found for target=$target" >&2; exit 2; } test -z "$target_name" && { echo "Error: No target name found for target=$target" >&2; exit 3; } - set -x - docker run --rm --mount="type=volume,source=$volume,destination=/data,ro=true" --mount="type=bind,source=$target_dir,destination=/data2" busybox /bin/sh -c "tar cvf '/data2/$target_name' /data && chown $(id -u):$(id -g) /data2/$target_name" - set +x + "$DOCKER" run --rm \ + --mount="type=volume,source=$volume,destination=/data,ro=true" \ + --mount="type=bind,source=$target_dir,destination=/data2" \ + busybox /bin/sh -c \ + "cd /data/ && tar cf '/data2/$target_name' ./* && chown $(id -u):$(id -g) /data2/$target_name" } restore() { - local volume - local target - local target_dir - local target_name target="$(realpath "$1")" target_dir="$(dirname "$target")" target_name="$(basename "$target")" volume="$2" - test -z "$(docker volume ls | grep "$volume")" && { echo "Error: No such volume, aborting" >&2; exit 1; } + test -z "$("$DOCKER" volume ls | grep "$volume")" && { echo "Error: No such volume, aborting" >&2; exit 1; } test -z "$target_dir" && { echo "Error: No base folder found for target=$target" >&2; exit 2; } test -z "$target_name" && { echo "Error: No target name found for target=$target" >&2; exit 3; } set -x - docker run --rm --mount="type=volume,source=$volume,destination=/data" --mount="type=bind,source=$target_dir,destination=/data2" busybox /bin/sh -c "rm -rf /data/* && cd / && tar xvf '/data2/$target_name'" + "$DOCKER" run --rm \ + --mount="type=volume,source=$volume,destination=/data" \ + --mount="type=bind,source=$target_dir,destination=/data2" \ + busybox /bin/sh -c \ + "cd /data/ && rm -rf ./* && tar xf '/data2/$target_name'" set +x } -main() { - local volumes - volumes="$(docker volume ls | tail -n+2 | egrep -v '[a-z]+\s+[0-9a-f]+$' | egrep -o '\s.+$' | sed 's/\s//g')" - while read -r line; do - echo "$line -> $line.tar" - backup "$line" "$line.tar" - done <<< "$volumes" +backup_all() { + "$DOCKER" volume ls \ + | awk '!/^[a-z]+ +[0-9a-f]+$/ && (NR>1) {print $2}' \ + | while read -r volume_name; do + echo "$volume_name -> $volume_name.tar" + backup "$volume_name" "$volume_name.tar" + done +} + +restore_all() { + for tarball in "$@"; do + volume_name="${tarball%.tar}" + echo "$volume_name -> $volume" + if ! "$DOCKER" volume inspect "$volume_name" 1>&2 2>/dev/null; then + echo "Error: no such volume $volume_name" >&2 + exit 4 + fi + restore "$tarball" "$volume_name" + done } "$@" diff --git a/test/mock-docker.sh b/test/mock-docker.sh new file mode 100755 index 0000000..c457764 --- /dev/null +++ b/test/mock-docker.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env sh + +volume() { + if [ "$1" = 'ls' ]; then + echo "DRIVER VOLUME NAME" + echo "local 00c674e3f3c1587d88c2ebf2f91da5843b9dddb3e8df272898bdfd4e596aef79" + echo "local $DOCKER_MOCK_VOLUME" + return 0 + elif [ "$2" = 'inspect' ]; then + if [ "$3" = "$DOCKER_MOCK_VOLUME" ]; then + return 0 + else + return 1 + fi + fi +} + + +if [ "$1" = "volume" ]; then + "$@" +else + docker "$@" +fi + diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000..f16c714 --- /dev/null +++ b/test/test.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + + +cd "$(dirname "$0")" || exit 1 + +VOLUME_NAME="$(dd if=/dev/random bs=6 count=1 | base64)" +docker volume create "$VOLUME_NAME" + +# shellcheck disable=SC2064 +trap "docker volume rm $VOLUME_NAME && rm -f $VOLUME_NAME.tar" EXIT + +docker run --rm --volume="$VOLUME_NAME:/data" alpine \ + sh -c 'echo "test" > /data/a.txt' +DOCKER=./mock-docker.sh DOCKER_MOCK_VOLUME="$VOLUME_NAME" \ + ../dvbackup.sh backup_all +stat "$VOLUME_NAME.tar" || exit 1 +docker run --rm --volume="$VOLUME_NAME:/data" alpine \ + sh -c 'rm /data/a.txt' || exit 1 +DOCKER=./mock-docker.sh DOCKER_MOCK_VOLUME="$VOLUME_NAME" \ + ../dvbackup.sh restore_all "$VOLUME_NAME.tar" || exit 1 +docker run --rm --volume="$VOLUME_NAME:/data" alpine \ + sh -c 'stat /data/a.txt' || exit 1 +exit 0