backup-init.sh 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/env bash
  2. set -e
  3. BACKUP_PATH="/backup"
  4. # Make sure that required directories exist
  5. mkdir -p "${BACKUP_PATH}"
  6. mkdir -p "/etc/crontabs"
  7. chown git:git /backup
  8. chmod 2770 /backup
  9. # [string] BACKUP_INTERVAL Period expression
  10. # [string] BACKUP_RETENTION Period expression
  11. if [ -z "${BACKUP_INTERVAL}" ]; then
  12. echo "Backup disabled: BACKUP_INTERVAL has not been found" 1>&2
  13. exit 1
  14. fi
  15. if [ -z "${BACKUP_RETENTION}" ]; then
  16. echo "Backup retention period is not defined, default to 7 days" 1>&2
  17. BACKUP_RETENTION='7d'
  18. fi
  19. # Parse BACKUP_INTERVAL environment variable and generate appropriate cron expression. Backup cron task will be run as scheduled.
  20. # Expected format: nu (n - number, u - unit) (eg. 3d means 3 days)
  21. # Supported units: h - hours, d - days, M - months
  22. parse_generate_cron_expression() {
  23. CRON_EXPR_MINUTES="*"
  24. CRON_EXPR_HOURS="*"
  25. CRON_EXPR_DAYS="*"
  26. CRON_EXPR_MONTHS="*"
  27. # shellcheck disable=SC2001
  28. TIME_INTERVAL=$(echo "${BACKUP_INTERVAL}" | sed -e 's/[hdM]$//')
  29. # shellcheck disable=SC2001
  30. TIME_UNIT=$(echo "${BACKUP_INTERVAL}" | sed -e 's/^[0-9]\+//')
  31. if [ "${TIME_UNIT}" = "h" ]; then
  32. if [ ! "${TIME_INTERVAL}" -le 23 ]; then
  33. echo "Parse error: Time unit 'h' (hour) cannot be greater than 23" 1>&2
  34. exit 1
  35. fi
  36. CRON_EXPR_MINUTES=0
  37. CRON_EXPR_HOURS="*/${TIME_INTERVAL}"
  38. elif [ "${TIME_UNIT}" = "d" ]; then
  39. if [ ! "${TIME_INTERVAL}" -le 30 ]; then
  40. echo "Parse error: Time unit 'd' (day) cannot be greater than 30" 1>&2
  41. exit 1
  42. fi
  43. CRON_EXPR_MINUTES=0
  44. CRON_EXPR_HOURS=0
  45. CRON_EXPR_DAYS="*/${TIME_INTERVAL}"
  46. elif [ "${TIME_UNIT}" = "M" ]; then
  47. if [ ! "${TIME_INTERVAL}" -le 12 ]; then
  48. echo "Parse error: Time unit 'M' (month) cannot be greater than 12" 1>&2
  49. exit 1
  50. fi
  51. CRON_EXPR_MINUTES=0
  52. CRON_EXPR_HOURS=0
  53. CRON_EXPR_DAYS="1"
  54. CRON_EXPR_MONTHS="*/${TIME_INTERVAL}"
  55. else
  56. echo "Parse error: BACKUP_INTERVAL expression is invalid" 1>&2
  57. exit 1
  58. fi
  59. echo "${CRON_EXPR_MINUTES} ${CRON_EXPR_HOURS} ${CRON_EXPR_DAYS} ${CRON_EXPR_MONTHS} *"
  60. }
  61. # Parse BACKUP_RETENTION environment variable and generate appropriate find command expression.
  62. # Expected format: nu (n - number, u - unit) (eg. 3d means 3 days)
  63. # Supported units: m - minutes, d - days
  64. parse_generate_retention_expression() {
  65. FIND_TIME_EXPR='mtime'
  66. # shellcheck disable=SC2001
  67. TIME_INTERVAL=$(echo "${BACKUP_RETENTION}" | sed -e 's/[mhdM]$//')
  68. # shellcheck disable=SC2001
  69. TIME_UNIT=$(echo "${BACKUP_RETENTION}" | sed -e 's/^[0-9]\+//')
  70. if [ "${TIME_UNIT}" = "m" ]; then
  71. if [ "${TIME_INTERVAL}" -le 59 ]; then
  72. echo "Warning: Minimal retention is 60m. Value set to 60m" 1>&2
  73. TIME_INTERVAL=60
  74. fi
  75. FIND_TIME_EXPR="mmin"
  76. elif [ "${TIME_UNIT}" = "h" ]; then
  77. echo "Error: Unsupported expression - Try: eg. 120m for 2 hours." 1>&2
  78. exit 1
  79. elif [ "${TIME_UNIT}" = "d" ]; then
  80. FIND_TIME_EXPR="mtime"
  81. elif [ "${TIME_UNIT}" = "M" ]; then
  82. echo "Error: Unsupported expression - Try: eg. 60d for 2 months." 1>&2
  83. exit 1
  84. else
  85. echo "Parse error: BACKUP_RETENTION expression is invalid" 1>&2
  86. exit 1
  87. fi
  88. echo "${FIND_TIME_EXPR} +${TIME_INTERVAL:-7}"
  89. }
  90. add_backup_cronjob() {
  91. CRONTAB_USER="${1:-git}"
  92. CRONTAB_FILE="/etc/crontabs/${CRONTAB_USER}"
  93. CRONJOB_EXPRESSION="${2:-}"
  94. CRONJOB_EXECUTOR="${3:-}"
  95. CRONJOB_EXECUTOR_ARGUMENTS="${4:-}"
  96. CRONJOB_TASK="${CRONJOB_EXPRESSION} /bin/sh ${CRONJOB_EXECUTOR} ${CRONJOB_EXECUTOR_ARGUMENTS}"
  97. if [ -f "${CRONTAB_FILE}" ]; then
  98. CRONJOB_EXECUTOR_COUNT=$(grep -c "${CRONJOB_EXECUTOR}" "${CRONTAB_FILE}" || exit 0)
  99. if [ "${CRONJOB_EXECUTOR_COUNT}" != "0" ]; then
  100. echo "Cron job already exists for ${CRONJOB_EXECUTOR}. Updating existing." 1>&2
  101. CRONJOB_TASK=$(echo "{CRONJOB_TASK}" | sed 's/\//\\\//g' )
  102. CRONJOB_EXECUTOR=$(echo "{CRONJOB_EXECUTOR}" | sed 's/\//\\\//g' )
  103. sed -i "/${CRONJOB_EXECUTOR}/c\\${CRONJOB_TASK}" "${CRONTAB_FILE}"
  104. return 0
  105. fi
  106. fi
  107. # Finally append new line with cron task expression
  108. echo "${CRONJOB_TASK}" >>"${CRONTAB_FILE}"
  109. }
  110. CRONTAB_USER=$(awk -v val="${PUID}" -F ":" '$3==val{print $1}' /etc/passwd)
  111. # Up to this point, it was desirable that interpreter handles the command errors and halts execution upon any error.
  112. # From now, we handle the errors our self.
  113. set +e
  114. RETENTION_EXPRESSION="$(parse_generate_retention_expression)"
  115. if [ -z "${RETENTION_EXPRESSION}" ]; then
  116. echo "Couldn't generate backup retention expression. Aborting backup setup" 1>&2
  117. exit 1
  118. fi
  119. # Backup rotator cron will run every 5 minutes
  120. add_backup_cronjob "${CRONTAB_USER}" "*/5 * * * *" "/app/gogs/docker/runtime/backup-rotator.sh" "'${BACKUP_PATH}' '${RETENTION_EXPRESSION}'"
  121. add_backup_cronjob "${CRONTAB_USER}" "$(parse_generate_cron_expression)" "/app/gogs/docker/runtime/backup-job.sh" "'${BACKUP_PATH}'"