#!/usr/bin/env bash

# This file managed by Chef.

# Requirements
# - Write access to logfile directory
# - Existence of source and destination volumes
# - Write access to destination share

DESTINATION_FILESYSTEM='10.40.0.62:/pools/nst-pool-01/corpsite_backup'
DESTINATION_FILESYSTEM_TYPE='nfs'
DESTINATION_MOUNT_ROOT='/mnt/nst'
DESTINATION_MOUNT_OPTIONS='nfsvers=3,vers=3'
SOURCE_DIR=('/var/www/html/wordpress/wp-content' '/mnt/efs/theorchard.com-git')
LOGFILE_NAME='/var/log/corpsite-backup.log'
DAILY_BACKUPS_TO_KEEP=7
WEEKLY_BACKUPS_TO_KEEP=4
MONTHLY_BACKUPS_TO_KEEP=12

function take_daily_backup {

  create_destination_mount
  check_logfile

  logmessage "Creating daily backup"

  # Clean up based on DAILY_BACKUPS_TO_KEEP
  logmessage "Cleaning up ${DESTINATION_MOUNT_ROOT}/${INTERVAL}, keeping ${DAILY_BACKUPS_TO_KEEP} days of backups"
  find "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" ! -path "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" -type f -mtime +"${DAILY_BACKUPS_TO_KEEP}" -exec rm -f {} +

  create_backup_archive
  unmount_destination_volume

}

function take_weekly_backup {

  create_destination_mount
  check_logfile

  logmessage "Creating weekly backup"

  # Clean up based on WEEKLY_BACKUPS_TO_KEEP
  export WEEKS_TO_DAYS=$(expr ${WEEKLY_BACKUPS_TO_KEEP} \* 7)
  logmessage "Cleaning up ${DESTINATION_MOUNT_ROOT}/${INTERVAL}, keeping ${WEEKLY_BACKUPS_TO_KEEP} weeks of backups"
  find "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" ! -path "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" -type f -mtime +"${WEEKS_TO_DAYS}" -exec rm -f {} +

  create_backup_archive
  unmount_destination_volume

}

function take_monthly_backup {

  create_destination_mount
  check_logfile

  logmessage "Creating monthly backup"

  # Clean up based on MONTHLY_BACKUPS_TO_KEEP. Monthly is approximate because we don't care THAT much.
  export MONTHS_TO_DAYS=$(expr ${MONTHLY_BACKUPS_TO_KEEP} \* 30)
  logmessage "Cleaning up ${DESTINATION_MOUNT_ROOT}/${INTERVAL}, keeping ${DAILY_BACKUPS_TO_KEEP} months of backups"
  find "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" ! -path "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" -type f -mtime +"${MONTHS_TO_DAYS}" -exec rm -f {} +

  create_backup_archive
  unmount_destination_volume

}

function create_destination_mount {
  
  # If on Linux, protect against NFS command hang
  if [ "$(uname)" == 'Linux' ]; then
    DF_COMMAND="timeout -k 5 5 df -h"
  else
    DF_COMMAND="df -h"
  fi

  if ${DF_COMMAND} | grep "^${DESTINATION_FILESYSTEM}" | grep -q "${DESTINATION_MOUNT_ROOT}$"; then
    logmessage "${DESTINATION_FILESYSTEM} is already mounted at ${DESTINATION_MOUNT_ROOT}. Continuing..."
  else
    logmessage "${DESTINATION_FILESYSTEM} is not mounted at ${DESTINATION_MOUNT_ROOT}. Attempting to mount..."
    mount -t ${DESTINATION_FILESYSTEM_TYPE} -o ${DESTINATION_MOUNT_OPTIONS} ${DESTINATION_FILESYSTEM} ${DESTINATION_MOUNT_ROOT}
    logmessage "Successfully mounted ${DESTINATION_FILESYSTEM} at ${DESTINATION_MOUNT_ROOT}."
  fi

  if [ -w "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" ]; then 
    logmessage "Destination directory exists and is writable. Continuing..."
    return 0
  elif [ -d "${DESTINATION_MOUNT_ROOT}/${INTERVAL}" ]; then
    logmessage "Destination directory exists and is not writable. Exiting..."
    exit 1
  else
    logmessage "${DESTINATION_MOUNT_ROOT}/${INTERVAL} not found. Creating..."
    mkdir "${DESTINATION_MOUNT_ROOT}/${INTERVAL}"
    logmessage "Successfully created ${DESTINATION_MOUNT_ROOT}/${INTERVAL}"
    return 0
  fi

}

function check_logfile {

  if [ -w "$(dirname ${LOGFILE_NAME})" ]; then 
    logmessage "Logfile directory is writable. Continuing..."
    return 0
  else
    printf '%s\n' "Logfile directory is not writable." && exit 1
  fi

}

function logmessage {
  printf '%s\n' " $(date +%c): ${1}" >> "${LOGFILE_NAME}"
  printf '%s\n' >> "${LOGFILE_NAME}"
}

function create_backup_archive {

  for dir in "${SOURCE_DIR[@]}"; do
    if [ -d "${dir}" ]; then
      logmessage "Creating archive of ${dir}..."
      tar -zcvf "${DESTINATION_MOUNT_ROOT}/${INTERVAL}/$(basename ${dir})-$(date +%Y-%d-%m).tar.gz" "${dir}"
      logmessage "Created archive of ${dir}..."
    elif [ -f "${dir}" ]; then
      logmessage "${dir} is a file. Creating archive of ${dir}... "
      tar -zcvf "${DESTINATION_MOUNT_ROOT}/${INTERVAL}/$(basename ${dir})-$(date +%Y-%d-%m).tar.gz" "${dir}"
      logmessage "Created archive of ${dir}..."
    else
      logmessage "Unsupported file type detected, or ${dir} may not exist"
    fi
  done

}

function unmount_destination_volume {
  
  # If on Linux, protect against NFS command hang
  if [ "$(uname)" == 'Linux' ]; then
    DF_COMMAND="timeout -k 5 5 df -h"
  else
    DF_COMMAND="df -h"
  fi

  if ${DF_COMMAND} | grep "^${DESTINATION_FILESYSTEM}" | grep -q "${DESTINATION_MOUNT_ROOT}$"; then
    logmessage "${DESTINATION_FILESYSTEM} is mounted at ${DESTINATION_MOUNT_ROOT}. Unmounting..."
    
    UMOUNT_COMMANDS=("umount" "umount -f" "umount -l")
    for option in "${UMOUNT_COMMANDS[@]}"; do
      if ${option} "${DESTINATION_MOUNT_ROOT}"; then 
        logmessage "${DESTINATION_MOUNT_ROOT} was unmounted successfully using ${option}."
        return 0
      else
        logmessage "${DESTINATION_MOUNT_ROOT} was not unmounted successfully. Trying more drastic measures..."
      fi
    done
  else
    logmessage "${DESTINATION_FILESYSTEM} is not mounted at ${DESTINATION_MOUNT_ROOT}. Nothing to do..."
    return 0
  fi

}

while getopts 'hi:' flag; do
  case "${flag}" in
  h) printf '%s\n'
     printf '%s\n' "This script creates and manages backups."
     printf '%s\n' "Specify the interval for backups with the -i option:"
     printf '%s\n'
     printf '%s\n' "-h: Print this message"
     printf '%s\n' "-i (required) Interval: options are daily, weekly, and monthly"
     printf '%s\n'
     printf '%s\n' "Example: /corpsite_backup.sh -i daily"
     exit 0 ;;
  i) INTERVAL="${OPTARG}" ;;
  *) printf "Unexpected option ${flag}\n"
     exit 1 ;;
  esac
done

case "${INTERVAL}" in
  daily)
    take_daily_backup ;;
  weekly)
    take_weekly_backup ;;
  monthly)
    take_monthly_backup ;;
  *)
    printf '%s\n' "Accepted intervals (-i option) are daily, weekly, and monthly"
    exit 1 ;;
esac
