diff --git a/Dockerfile b/Dockerfile index 9b2b7b7..0f6c557 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,44 +3,6 @@ LABEL maintainer="jmm@yavook.de" # Previous work: https://github.com/wernight/docker-duplicity -ENV \ - ################# - # BACKUP POLICY # - ################# - # - # when to run backups - # default: "36 03 * * * " <=> daily at 3:36 am - SCHEDULE_BACKUP="36 03 * * * " \ - # - # when to remove failed transactions - # default: "36 04 * * * " <=> daily at 04:36 am - SCHEDULE_CLEANUP="36 04 * * * " \ - # - # how often to opt for a full backup - # default: 4M <=> every 4 months - FULL_BACKUP_FREQUENCY=4M \ - # - # how long to keep backups at all - # default: 9M <=> 9 months - BACKUP_RETENTION_TIME=9M \ - # - # how many full backup chains with incrementals to keep - # default: 1 - KEEP_NUM_FULL_CHAINS=1 \ - \ - ################## - # CRON SCHEDULES # - ################## - # - # when to remove old full backup chains - # default: "36 05 * * SAT" <=> every saturday at 05:36 am - SCHEDULE_RMFULL="36 05 * * SAT" \ - # - # when to remove old incremental backups - # default: "36 05 * * SUN" <=> every sunday at 05:36 am - SCHEDULE_RMINCR="36 05 * * SUN" - - RUN set -ex; \ \ apk add --no-cache \ @@ -112,8 +74,74 @@ RUN set -ex; \ pip3 uninstall -y wheel; \ apk del --purge .build-deps +VOLUME ["/backup/source", "/root/.cache/duplicity"] + +ENV \ + ################# + # BACKUP POLICY # + ################# + # + # when to run backups + # default: "36 03 * * *" <=> daily at 3:36 am + SCHEDULE_BACKUP="36 03 * * *" \ + # + # when to remove failed transactions + # default: "36 04 * * *" <=> daily at 04:36 am + SCHEDULE_CLEANUP="36 04 * * *" \ + # + # how often to opt for a full backup + # default: 4M <=> every 4 months + FULL_BACKUP_FREQUENCY=4M \ + # + # how long to keep backups at all + # default: 9M <=> 9 months + BACKUP_RETENTION_TIME=9M \ + # + # how many full backup chains with incrementals to keep + # default: 1 + KEEP_NUM_FULL_CHAINS=1 \ + # + # where to put backups + # default: "file:///backup/target" <=> in a host-mounted volume + BACKUP_TARGET="file:///backup/target" \ + \ + ############## + # ENCRYPTION # + ############## + # + # GnuPG key-id as specified by https://www.gnupg.org/documentation/manpage.html#sec-2-6 + GPG_KEY_ID="" \ + # + GPG_PASSPHRASE="" \ + \ + ###################### + # ADDITIONAL OPTIONS # + ###################### + # + # when to remove old full backup chains + # default: "36 05 * * SAT" <=> every saturday at 05:36 am + SCHEDULE_RMFULL="36 05 * * SAT" \ + # + # when to remove old incremental backups + # default: "36 05 * * SUN" <=> every sunday at 05:36 am + SCHEDULE_RMINCR="36 05 * * SUN" \ + # + # size of individual duplicity data volumes + # default: 1024 <=> 1GiB + BACKUP_VOLSIZE=1024 \ + # + # Additional options for "duplicity" command + OPTIONS_BACKUP="" \ + # + # Additional options for "duplicity cleanup" command + OPTIONS_CLEANUP="" \ + # + # Additional options for "duplicity remove-older-than" command + OPTIONS_RMFULL="" \ + # + # Additional options for "duplicity remove-all-inc-of-but-n-full" command + OPTIONS_RMINCR="" + COPY run.sh /usr/local/bin/do-plicity -VOLUME ["/backup/source", "/backup/target", "/root/.cache/duplicity", "/root/.gnupg"] - CMD ["do-plicity"] diff --git a/run.sh b/run.sh index 70fff71..d3a0a03 100755 --- a/run.sh +++ b/run.sh @@ -1,47 +1,112 @@ #!/bin/sh -get_cron_line() { +############# +# CONSTANTS # +############# + +env_exe="$(command -v env)" +ionice_exe="$(command -v ionice)" +duplicity_exe="$(command -v duplicity)" + +if [ -n "${GPG_KEY_ID}" ]; then + # gpg key given + env_changes="PASSPHRASE='${GPG_PASSPHRASE}'" + encrypt_opts="--encrypt-key='${GPG_KEY_ID}'" +else + # no key given + env_changes="" + encrypt_opts="--no-encryption" +fi + +############# +# FUNCTIONS # +############# + +trim_options() { + # if args are given, trim whitespace, then add a space in front + if [ -n "${1}" ]; then + echo " $( echo "${1}" | xargs )" + fi +} + +print_command() { task="${1}" shift 1 - cmdline='/bin/ionice -c 3 /usr/bin/duplicity --no-encryption' + if [ -n "${env_changes}" ]; then + # should change environment + cmdline="${env_exe} ${env_changes} " + else + cmdline="" + fi + + cmdline="${cmdline}${ionice_exe} -c 3 ${duplicity_exe} ${encrypt_opts}" case "${task}" in backup) - cmdline="${cmdline} --allow-source-mismatch --volsize 1024 --full-if-older-than ${FULL_BACKUP_FREQUENCY} /backup/source" + cmdline="${cmdline} --allow-source-mismatch --volsize '${BACKUP_VOLSIZE}' --full-if-older-than '${FULL_BACKUP_FREQUENCY}'" + cmdline="${cmdline}$( trim_options "${OPTIONS_BACKUP}" )" + cmdline="${cmdline} /backup/source" ;; - clean) + cleanup) cmdline="${cmdline} cleanup --force" + cmdline="${cmdline}$( trim_options "${OPTIONS_CLEAN}" )" ;; rmfull) - cmdline="${cmdline} remove-older-than ${BACKUP_RETENTION_TIME} --force" + cmdline="${cmdline} remove-older-than '${BACKUP_RETENTION_TIME}' --force" + cmdline="${cmdline}$( trim_options "${OPTIONS_RMFULL}" )" ;; rmincr) - cmdline="${cmdline} remove-all-inc-of-but-n-full ${KEEP_NUM_FULL_CHAINS} --force" + cmdline="${cmdline} remove-all-inc-of-but-n-full '${KEEP_NUM_FULL_CHAINS}' --force" + cmdline="${cmdline}$( trim_options "${OPTIONS_RMINCR}" )" ;; esac - cmdline="${cmdline} file:///backup/target" + cmdline="${cmdline} '${BACKUP_TARGET}'" echo "${cmdline}" } -prepare_crontab() { - echo "${SCHEDULE_BACKUP}" "$(get_cron_line backup)" - echo "${SCHEDULE_CLEANUP}" "$(get_cron_line clean)" - echo "${SCHEDULE_RMFULL}" "$(get_cron_line rmfull)" - echo "${SCHEDULE_RMINCR}" "$(get_cron_line rmincr)" +print_cron_schedule() { + min="$( echo "${1}" | cut -d' ' -f1 )" + hour="$( echo "${1}" | cut -d' ' -f2 )" + day="$( echo "${1}" | cut -d' ' -f3 )" + month="$( echo "${1}" | cut -d' ' -f4 )" + weekday="$( echo "${1}" | cut -d' ' -f5 )" + command="${2}" + + printf '%-8s%-8s%-8s%-8s%-8s%s' "${min}" "${hour}" "${day}" "${month}" "${weekday}" "${command}" } -get_crontab() { +print_cron_header() { + # don't split the '#' from 'min' + print_cron_schedule '#_min hour day month weekday' 'command' | tr '_' ' ' +} + +print_crontab() { echo '# crontab generated for kiwi-backup' printf '# generation time: '; date echo '#' - prepare_crontab + echo "$( print_cron_header )" + echo "$( print_cron_schedule "${SCHEDULE_BACKUP}" "$( print_command backup )" )" + echo "$( print_cron_schedule "${SCHEDULE_CLEANUP}" "$( print_command cleanup )" )" + echo "$( print_cron_schedule "${SCHEDULE_RMFULL}" "$( print_command rmfull )" )" + echo "$( print_cron_schedule "${SCHEDULE_RMINCR}" "$( print_command rmincr )" )" } +######## +# MAIN # +######## + +if [ "${1}" = '-n' ]; then + # dry-run + print_crontab + exit 0 +fi + # replace crontab, start crond -get_crontab | crontab - +print_crontab | crontab - crond -fl 8 +exit 0