#!/bin/bash # This script contains the common code for the autocommands. MYID=$(id -u) MYGRP=$(id -g) _autocommandrun_volume() { local command="$1" local volume_name="$2" local backup_folder="$3" case "$command" in create) if docker volume ls | grep -q "${volume_name}"; then echo "Volume ${volume_name} already exists - leaving unchanged" return fi echo "Creating volume ${volume_name}" docker volume create "${volume_name}" ;; destroy) echo "Destroying volume ${volume_name}" docker volume rm "${volume_name}" ;; backup) echo "Backing up volume ${volume_name}" docker run --rm -v "${volume_name}":/volume -v "${backup_folder}":/backup debian bash -c "tar -czvf /backup/backup.tgz -C /volume . && chown -R $MYID:$MYGRP /backup" ;; restore) echo "Restoring volume ${volume_name}" docker volume rm "${volume_name}" docker volume create "${volume_name}" docker run --rm -v "${volume_name}":/volume -v "${backup_folder}":/backup debian bash -c "tar -xzvf /backup/backup.tgz -C /volume --strip-components=1" ;; esac } _autocommandrun_path() { local command="$1" local path="$2" local backup_folder="$3" case "$command" in create) if [ -d "${path}" ]; then echo "Path ${path} already exists - unchanged" return fi echo "Creating path ${path}" mkdir -p "${path}" ;; destroy) echo "Destroying path ${path}" local path_parent; path_parent=$(dirname "${path}") local path_child; path_child=$(basename "${path}") if [ -d "${path_parent}/${path_child}" ]; then docker run --rm -v "${path_parent}":/volume debian bash -c "rm -rfv /volume/${path_child}" || echo "Failed to destroy path ${path}" else echo "Path ${path} does not exist - nothing to destroy" fi ;; backup) echo "Backing up path ${path}" if [ -d "${path}" ]; then docker run --rm -v "${path}":/path -v "${backup_folder}":/backup debian bash -c "tar -czvf /backup/backup.tgz -C /path . && chown -R $MYID:$MYGRP /backup" else echo "Path ${path} does not exist - nothing to backup" fi ;; restore) if [ ! -f "${backup_folder}/backup.tgz" ]; then echo "Backup file ${backup_folder}/backup.tgz does not exist - nothing to restore" else echo "Clearing existing data in path ${path}" docker run --rm -v "${path}":/path debian bash -c "rm -rfv /path/{*,.*}" echo "Restoring path ${path} from backup file ${backup_folder}/backup.tgz" tar -xzvf "${backup_folder}/backup.tgz" -C "${path}" --strip-components=1 fi ;; esac } _autocommandrun_file() { local command="$1" local filepath="$2" local backup_folder="$3" case "$command" in create) local file_parent; file_parent=$(dirname "${filepath}") local file_name; file_name=$(basename "${filepath}") if [ ! -d "${file_parent}" ]; then echo "Parent directory ${file_parent} of ${file_name} does not exist - creating" mkdir -p "${file_parent}" fi ;; destroy) rm -f "${filepath}" ;; backup) echo "Backing up file ${filepath}" local file_parent; file_parent=$(dirname "${filepath}") local file_name; file_name=$(basename "${filepath}") if [ -f "${file_parent}/${file_name}" ]; then docker run --rm -v "${file_parent}":/volume -v "${backup_folder}":/backup debian bash -c "cp /volume/${file_name} /backup/${file_name} && chown -R $MYID:$MYGRP /backup" else echo "File ${filepath} does not exist - nothing to backup" fi ;; restore) echo "Restoring file ${filepath}" local file_name; file_name=$(basename "${filepath}") rm -f "${filepath}" || die "Unable to remove existing file ${filepath}, restore failed." cp "${backup_folder}/${file_name}" "${filepath}" || die "Unable to copy file ${backup_folder}/${file_name} to ${filepath}, restore failed." ;; esac } _autocommandparse() { # first argument is the command # if the command is backup or restore, then the last two arguments are the backup file and the temporary path # all other arguments are of form: # key=value # where key can be one of volume, path or file. # value is the path or volume name. # we iterate over the key=value arguments, and for each we call: # autorun local command="$1" shift local backup_temp_path="$1" shift echo "autocommandparse: command=$command backup_temp_path=$backup_temp_path" # Extract the backup file and temp path (last two arguments) local args=("$@") local arg_count=${#args[@]} # Process all key=value pairs for ((i=0; i<$arg_count; i++)); do local pair="${args[$i]}" # Skip if not in key=value format if [[ "$pair" != *"="* ]]; then continue fi local key="${pair%%=*}" local value="${pair#*=}" # create backup folder unique to key/value. local bfolder; bfolder=$(echo "${key}_${value}" | tr -cd '[:alnum:]_-') local targetpath="${backup_temp_path}/${bfolder}" mkdir -p "${targetpath}" # Key must be one of volume, path or file case "$key" in volume) _autocommandrun_volume "$command" "$value" "$targetpath" ;; path) _autocommandrun_path "$command" "$value" "$targetpath" ;; file) _autocommandrun_file "$command" "$value" "$targetpath" ;; *) _die "Unknown key $key passed to auto${command}. We only support volume, path and file." ;; esac done } datacreate() { _autocommandparse create none "$@" } datadestroy() { _autocommandparse destroy none "$@" } databackup() { _check_required_env_vars "BACKUP_FILE" "TEMP_DIR" BACKUP_TEMP_PATH="$TEMP_DIR/backup" mkdir -p "$BACKUP_TEMP_PATH" echo "_autocommandparse [backup] [$BACKUP_TEMP_PATH]" "$@" _autocommandparse backup "$BACKUP_TEMP_PATH" "$@" tar zcvf "$BACKUP_FILE" -C "$BACKUP_TEMP_PATH" . } datarestore() { _check_required_env_vars "BACKUP_FILE" "TEMP_DIR" BACKUP_TEMP_PATH="$TEMP_DIR/restore" echo "_autocommandparse [restore] [$BACKUP_TEMP_PATH]" "$@" mkdir -p "$BACKUP_TEMP_PATH" tar zxvf "$BACKUP_FILE" -C "$BACKUP_TEMP_PATH" --strip-components=1 _autocommandparse restore "$BACKUP_TEMP_PATH" "$@" }