mirror of
https://github.com/luzifer-docker/pvc-rsync.git
synced 2024-12-20 19:21:16 +00:00
Initial version
This commit is contained in:
commit
cb9ec1325a
2 changed files with 221 additions and 0 deletions
24
Dockerfile
Normal file
24
Dockerfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
FROM bash:5 as prefetch
|
||||
|
||||
ARG DUMB_INIT_VERSION=1.2.5
|
||||
|
||||
RUN set -ex \
|
||||
&& apk --no-cache add \
|
||||
curl \
|
||||
&& curl -sSfLo /dumb-init "https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64" \
|
||||
&& chmod 0755 /dumb-init
|
||||
|
||||
|
||||
FROM bash:5
|
||||
|
||||
RUN set -ex \
|
||||
&& apk --no-cache add \
|
||||
coreutils \
|
||||
grep \
|
||||
openssh \
|
||||
rsync
|
||||
|
||||
COPY --from=prefetch /dumb-init /usr/local/bin/
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
197
docker-entrypoint.sh
Executable file
197
docker-entrypoint.sh
Executable file
|
@ -0,0 +1,197 @@
|
|||
#!/usr/local/bin/dumb-init bash
|
||||
set -euo pipefail
|
||||
|
||||
: ${BASE_DIR:=.} # Where to create the backup dir
|
||||
: ${EXIT_ON_ERROR:=false} # Exit on backup error (default keep running)
|
||||
: ${HETZNER_WORKAROUND:=false} # Hetzner StorageBox needs sftp for symlinks
|
||||
: ${INTERVAL:=3600} # When to backup (3600 = *:00, 1800 = *:00,30)
|
||||
: ${KEEP_LAST_N:=0} # How many backups to keep
|
||||
: ${LATEST_LINK:=latest} # How to name the latest link
|
||||
: ${LOCAL_DIR:=/data} # Where to find the data to backup
|
||||
: ${NAME_SCHEMA:=%Y-%m-%d_%H-%M-%S} # How to name backup dirs, make sure to make it sortable when using KEEP_LAST_N, do not use spaces
|
||||
: ${ONESHOT:=false} # Run only once (backup only), set INTERVAL to 1 to execute directly on start
|
||||
: ${REMOTE_HOST:=} # Where to send the backups
|
||||
: ${SKIP_RESTORE_ON:=} # File to check, if exists restore will be skipped
|
||||
: ${SSH_CONFIG_MOUNT:=~/.ssh-dist} # Where to search for ~/.ssh contents to copy into ~/.ssh (Secret mountPath)
|
||||
|
||||
function cleanup_old_backups() {
|
||||
info "Starting cleanup of backups..."
|
||||
|
||||
for backup in $(ssh "${REMOTE_HOST}" -- ls "${BASE_DIR}" | grep -v "${LATEST_LINK}" | sort | head --lines=-${KEEP_LAST_N}); do
|
||||
ssh "${REMOTE_HOST}" -- rm -rf "${BASE_DIR}/${backup}" || {
|
||||
error "Failed to delete backup ${backup}"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
function ensure_basedir() {
|
||||
info "Ensuring base-dir..."
|
||||
|
||||
ssh "${REMOTE_HOST}" -- mkdir -p "${BASE_DIR}" || {
|
||||
error "Failed to create base-dir."
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
function error() {
|
||||
log E "$@"
|
||||
}
|
||||
|
||||
function exit_error() {
|
||||
if [[ $EXIT_ON_ERROR == true ]]; then
|
||||
fatal "$@"
|
||||
return 0
|
||||
fi
|
||||
|
||||
error "$@"
|
||||
}
|
||||
|
||||
function fatal() {
|
||||
log F "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function import_ssh_dir() {
|
||||
info "Importing ~/.ssh from ${SSH_CONFIG_MOUNT}"
|
||||
|
||||
mkdir -p ~/.ssh || {
|
||||
error "Failed to create ~/.ssh dir."
|
||||
return 1
|
||||
}
|
||||
|
||||
rsync -a "${SSH_CONFIG_MOUNT}/" ~/.ssh/
|
||||
chown $(id -u) ~/.ssh/*
|
||||
chmod 0600 ~/.ssh/*
|
||||
}
|
||||
|
||||
function info() {
|
||||
log I "$@"
|
||||
}
|
||||
|
||||
function link_latest() {
|
||||
info "Creating latest link..."
|
||||
|
||||
local dest="$1"
|
||||
local link="$2"
|
||||
|
||||
if [[ $HETZNER_WORKAROUND == true ]]; then
|
||||
echo -e "rm ${link}\nsymlink ${dest} ${link}" | sftp "${REMOTE_HOST}" && return 0 || {
|
||||
error "Renewing latest-link (sftp)."
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
ssh "${REMOTE_HOST}" -- ln -sf "${dest}" "${link}" || {
|
||||
error "Renewing latest-link (ssh)."
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
function log() {
|
||||
local level=$1
|
||||
shift
|
||||
echo "[$(date +%H:%M:%S)][$level] $@" >&2
|
||||
}
|
||||
|
||||
function main() {
|
||||
[[ -n $REMOTE_HOST ]] || fatal "No REMOTE_HOST set"
|
||||
|
||||
if [[ -d $SSH_CONFIG_MOUNT ]]; then
|
||||
import_ssh_dir || fatal "Failed importing SSH config."
|
||||
fi
|
||||
|
||||
case "${1:-help}" in
|
||||
backup)
|
||||
while true; do
|
||||
sleep $((INTERVAL - $(date +%s) % INTERVAL))
|
||||
run_backup || exit_error "Backup failed."
|
||||
|
||||
if [ $KEEP_LAST_N -gt 0 ]; then
|
||||
cleanup_old_backups
|
||||
fi
|
||||
|
||||
[[ $ONESHOT != true ]] || {
|
||||
info "ONESHOT activated, exit now"
|
||||
break
|
||||
}
|
||||
done
|
||||
;;
|
||||
|
||||
restore)
|
||||
run_restore || exit_error "Restore failed."
|
||||
;;
|
||||
|
||||
*)
|
||||
usage
|
||||
fatal "Action ${1:-help} called"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function run_backup() {
|
||||
local current="$(date +${NAME_SCHEMA})"
|
||||
local dest="${BASE_DIR}/${current}"
|
||||
local link="${BASE_DIR}/${LATEST_LINK}"
|
||||
|
||||
info "Starting backup..."
|
||||
|
||||
ensure_basedir || {
|
||||
error "Failed to ensure base-dir."
|
||||
return 1
|
||||
}
|
||||
|
||||
info "Synchronizing backup..."
|
||||
rsync -av --delete \
|
||||
"${LOCAL_DIR}/" \
|
||||
--link-dest "../${LATEST_LINK}/" \
|
||||
"${REMOTE_HOST}:${dest}/" || {
|
||||
error "Failed to sync backup-dir."
|
||||
return 1
|
||||
}
|
||||
|
||||
link_latest "${current}" "${link}" || {
|
||||
error "Failed to create latest link."
|
||||
return 1
|
||||
}
|
||||
|
||||
info "Backup finished."
|
||||
}
|
||||
|
||||
function run_restore() {
|
||||
if [[ -n $SKIP_RESTORE_ON ]] && [[ -e $SKIP_RESTORE_ON ]]; then
|
||||
info "Check-file ${SKIP_RESTORE_ON} exists, skipping restore."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local link="${BASE_DIR}/${LATEST_LINK}"
|
||||
|
||||
info "Starting restore..."
|
||||
|
||||
mkdir -p "${LOCAL_DIR}" || {
|
||||
error "Failed to ensure local dir..."
|
||||
return 1
|
||||
}
|
||||
|
||||
rsync -av --delete \
|
||||
"${REMOTE_HOST}:${link}/" \
|
||||
"${LOCAL_DIR}/" || {
|
||||
error "Failed to sync remote data..."
|
||||
return 1
|
||||
}
|
||||
|
||||
info "Restore finished."
|
||||
}
|
||||
|
||||
function usage() {
|
||||
cat >&2 <<EOF
|
||||
Usage:
|
||||
docker run --rm -ti \
|
||||
-v /mydata:/data:ro \
|
||||
-e REMOTE_HOST=user@host \
|
||||
pvc-rsync <backup|restore>
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
main "$@"
|
Loading…
Reference in a new issue