From e1d20e237ade411957638f3ad7dc8f6d2fb6d135 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Fri, 16 Mar 2018 16:22:28 +0000 Subject: [PATCH] Add docker-volume-backup and docker-volume-restore binaries --- bin/docker-volume-backup | 97 ++++++++++++++++++++++++++++++++++ bin/docker-volume-restore | 106 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100755 bin/docker-volume-backup create mode 100755 bin/docker-volume-restore diff --git a/bin/docker-volume-backup b/bin/docker-volume-backup new file mode 100755 index 0000000..4fc3322 --- /dev/null +++ b/bin/docker-volume-backup @@ -0,0 +1,97 @@ +#! /usr/bin/env bash +set -e + +abs_dirname () { + local path="$1" + local cwd + cwd="$(pwd)" + + while [ -n "$path" ]; do + cd "${path%/*}" 2>/dev/null + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + +abs_path () { + local path="$1" + echo "$(cd "$(abs_dirname "$path")" && pwd)/$(basename "$path")" +} + +resolve_link () { + $(type -p greadlink readlink | head -1) "$1" +} + +resolve_dest () { + local dest="$1" + + if [ -z "$dest" ]; then + dest="$(pwd)/${volname}_$(date '+%Y-%m-%d_%H-%M-%S').tar.gz" + fi + + dest="$(abs_path "$dest")" + + if [ -f "$dest" ]; then + echo "ERROR: \"${dest}\" already exists." 1>&2 + exit 1 + fi + + echo "$dest" +} + +resolve_dest_dir () { + local dest="$1" + local dir + + dir="$(dirname "$dest")" + + if [ ! -d "$dir" ]; then + echo "ERROR: \"$dir\" is not a directory." + fi + + echo "$dest_dir" +} + +help () { + echo "usage: docker-volume-backup []" + echo "" + echo "Backs up contents of to a gzipped tar archive." + echo "" + echo "If optional argument is given, it should" + echo "end with \".tar.gz\". If is not given," + echo "resulting file with be named: _.tar.gz" +} + +main () { + local volname="$1" + local dest="$2" + local dest_dir + + if [ -z "$volname" ]; then + help 1>&2 + exit 1 + fi + + if ! docker volume inspect "$volname" &>/dev/null; then + echo "ERROR: Volume \"${volname}\" does not exist." 1>&2 + exit 1 + fi + + dest="$(resolve_dest "$dest")" + dest_dir="$(resolve_dest_dir "$dest")" + dest_file="$(basename "$dest")" + + echo "Starting volume backup to: ${dest}" + docker run --rm \ + --volume "${volname}:/source/${volname}" \ + --volume "${dest_dir}:/target" \ + alpine:latest \ + sh -c "cd /source && tar -cvzf \"/target/${dest_file}\" \"${volname}\"" + + echo "Volume \"${volname}\" was backed up to: ${dest}" +} + +main "$@" diff --git a/bin/docker-volume-restore b/bin/docker-volume-restore new file mode 100755 index 0000000..4f6127d --- /dev/null +++ b/bin/docker-volume-restore @@ -0,0 +1,106 @@ +#! /usr/bin/env bash +set -e + +abs_dirname () { + local path="$1" + local cwd + cwd="$(pwd)" + + while [ -n "$path" ]; do + cd "${path%/*}" 2>/dev/null + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + +abs_path () { + local path="$1" + echo "$(cd "$(abs_dirname "$path")" && pwd)/$(basename "$path")" +} + +resolve_link () { + $(type -p greadlink readlink | head -1) "$1" +} + +resolve_archive() { + local archive="$1" + + archive="$(abs_path "$archive")" + + if [ ! -f "$archive" ]; then + echo "ERROR: \"${archive}\" is not a file." 1>&2 + exit 1 + fi + + echo "$archive" +} + +resolve_volname () { + local archive="$1" + local file + local dir + + file="$(basename "$archive")" + dir="$(dirname "$archive")" + + docker run --rm \ + --volume "${dir}:/source" \ + alpine:latest \ + sh -c "tar --exclude=\"*/*/*\" -tzf \"/source/${file}\" \ + | grep -v '^[^/]\+/[^/]\+$' | sed s'/.$//'" 2>/dev/null +} + +help () { + echo "usage: docker-volume-restore []" + echo "" + echo "Input should be a *.tar.gz producded by the" + echo "docker-volume-backup command." +} + +main () { + local archive="$1" + local volname="$2" + local archive_volname + local archive_file + local archive_dir + + if [ -z "$archive" ]; then + help 1>&2 + exit 1 + fi + + archive="$(resolve_archive "$archive")" + archive_file="$(basename "$archive")" + archive_dir="$(dirname "$archive")" + archive_volname="$(resolve_volname "$archive")" + + if [ "$(echo "$archive_volname" | wc -l)" -gt 1 ]; then + echo "ERROR: \"$archive_file\" has more than one root directory." 1>&2 + exit 1 + fi + + if [ -z "$volname" ]; then + volname="$archive_volname" + fi + + if docker volume inspect "$volname" &>/dev/null; then + echo "ERROR: Volume \"${volname}\" already exists." 1>&2 + exit 1 + fi + + echo "Restoring volume ${volume} from: ${archive_file}" + docker volume create "$volname" + docker run --rm \ + --volume "${volname}:/target" \ + --volume "${archive_dir}:/source" \ + alpine:latest \ + sh -c "cd /target && tar --strip-components=1 \ + -xvzf \"/source/${archive_file}\"" + + echo "Volume \"${volname}\" was restored from: ${archive_file}" +} + +main "$@"