# OHWR backup script
# This script requires:
#   * ruby interpreter (so rake can be executed)
#   * subversion (client+server)
#   * git
#   * wget client (avaliable from the command line)
#   * rdiff-backup v1.2.8
#
# The user executing the rakefile must have:
#   * Permissions to administrate svn repositories on LOCAL_REPOSITORY_PATH
#   * Write permissions on LOCAL_*_PATH
#   * Permissions to execute ruby, wget and rdiff-backup
#   * The client must have a correctly configured .ssh/configure folder, including username, passwords & ports
#     set up for accessing the owhr site (production or staging)
#   * The account on the ohwr server must be a member of the "sympa", "rails" and "backup" groups
#
# Usage:
#
#  svn backup of all svn repositories on ohwr. Does not require ssh access to ohwr
#  warning: the first time it can take long
#    rake backup:svn
#
#  Backup all git repositories on ohwr. Does not require ssh access to ohwr
#    rake backup:git
#
#  Update or create a database dump backup. Requires ssh access to ohwr
#    rake backup:db
#
#  Update or create a file backup. Requires ssh access to ohwr
#    rake backup:files
#
#  Updates or creates a mailing list backup. Requires ssh access to ohwr
#    rake backup:sympa
#
#  All of the above
#    rake backup:all

require 'rake'

# =========== CUSTOMIZABLE SETTINGS =============

# Environment being used. Can be either 'production' or 'staging'
ENVIRONMENT = 'production'

# States where the backups will be stored.
# LOCAL_BACKUP_PATH is only used here, so it can be ignored if required.
LOCAL_BACKUP_PATH = '/home/fjuan/backup'
LOCAL_SVNLIST_PATH = "#{LOCAL_BACKUP_PATH}/svn_list.txt"
LOCAL_GITLIST_PATH = "#{LOCAL_BACKUP_PATH}/git_list.txt"
LOCAL_REPOSITORY_PATH = "#{LOCAL_BACKUP_PATH}/svn"
LOCAL_GIT_PATH = "#{LOCAL_BACKUP_PATH}/git"
LOCAL_DB_PATH = "#{LOCAL_BACKUP_PATH}/db"
LOCAL_FILES_PATH = "#{LOCAL_BACKUP_PATH}/redmine-files"
LOCAL_SYMPA_PATH = "#{LOCAL_BACKUP_PATH}/sympa"

# Defines the names used on the .ssh/config file for accessing the servers via ssh
SSH_ALIASES = {
  'staging' => 'staging',
  'production' => 'ohwr'
}

# Defines the usernames to be used on ssh access. This is needed in order to use rsync
SSH_USERS = {
  'staging' => 'egarcia',
  'production' => 'egarcia'
}

# =========== END OF CUSTOMIZABLE SETTINGS =============

# Server urls. Used for obtaining the list of svn repositories
SERVER_URLS = {
  'staging' => 'http://ohwr.staging.es',
  'production' => 'http://www.ohwr.org'
}

# Repository urls. Used for invoking the svnsync command
REMOTE_REPOSITORY_URLS = {
  'staging' => 'http://svn.ohwr.staging.es',
  'production' => 'http://svn.ohwr.org'
}

def log(message, level = 0)
  puts ""
  puts "#{'==' * level}> #{message}"
  puts ""
end

def remote_repository_url(repository)
  "#{REMOTE_REPOSITORY_URLS[ENVIRONMENT]}/#{repository}"
end

def server_url
  SERVER_URLS[ENVIRONMENT]
end

def ssh_alias
  SSH_ALIASES[ENVIRONMENT]
end

def ssh_user
  SSH_USERS[ENVIRONMENT]
end

def local_repository_list
  @local_repository_list ||= Dir.entries(LOCAL_REPOSITORY_PATH) - ['.', '..']
end

def repository_exists?(repository)
  local_repository_list.include? repository
end

def init_repository(repository)
  log "Initializing repository #{repository}", 2

  path = File.join(LOCAL_REPOSITORY_PATH, repository)
  pre_file_path = "#{path}/hooks/pre-revprop-change"

  sh "svnadmin create #{path}"
  sh "echo '#!/bin/sh' >#{pre_file_path}"
  sh "chmod +x #{pre_file_path}"
  sh "svnsync init file://#{path} #{remote_repository_url(repository)}"
end

def sync_repository(repository)
  log "Syncing repository #{repository}", 2
  path = File.join(LOCAL_REPOSITORY_PATH, repository)
  sh "svnsync sync file://#{path}"
end

def local_git_list
  @local_git_list ||= Dir.entries(LOCAL_GIT_PATH) - ['.', '..']
end

def git_exists?(repository)
  local_git_list.include? repository
end

def init_git_repository(repository)
  log "Initializing git repository #{repository}", 2
  path = File.join(LOCAL_GIT_PATH)
  sh "cd #{path} && git clone #{repository}"
end

def sync_git_repository(repository)
  log "Syncing repository #{repository}", 2
  path = File.join(LOCAL_GIT_PATH, repository)
  sh "cd #{path} && git pull"
end

def rdiff_get(source, destination)
  sh "rdiff-backup #{ssh_user}@#{ssh_alias}::#{source} #{destination}"
end

namespace :backup do

  desc "Creates initial folders if they don't exist"
  task :prepare_folders do
    log "Preparing local folders"
    [LOCAL_REPOSITORY_PATH, LOCAL_GIT_PATH, LOCAL_DB_PATH, LOCAL_FILES_PATH, File.join(LOCAL_SYMPA_PATH,'arc'), File.join(LOCAL_SYMPA_PATH,'list_data')].each do |path|
      sh "mkdir -p #{path}"
    end
  end

  desc "Implements the svn repositories backup"
  task :svn => :prepare_folders do
    log "Getting svn list from #{server_url}"
    sh "wget -m -nd #{server_url}/svn_list.txt -O #{LOCAL_SVNLIST_PATH}"

    log "Initializing new SVN repositories and updating existing ones"
    file = File.new(LOCAL_SVNLIST_PATH, "r")
    while (repository = file.gets)
      repository.strip!
      init_repository(repository) unless repository_exists?(repository)
      sync_repository(repository)
    end
    file.close
  end
  
  desc "Implements the git repositories backup"
  task :git => :prepare_folders do
    log "Getting git list from #{server_url}"
    sh "wget -m -nd #{server_url}/git_list.txt -O #{LOCAL_GITLIST_PATH}"

    log "Initializing new GIT repositories and updating existing ones"
    file = File.new(LOCAL_GITLIST_PATH, "r")
    while (repository = file.gets)
      repository.strip!
      repository_folder = repository.split('/').last.gsub('.git','')
      init_git_repository(repository) unless git_exists?(repository_folder)
      sync_git_repository(repository_folder)
    end
    file.close
  end

  desc "Implements the database backup"
  task :db => :prepare_folders do
    log "Backing up databases"
    rdiff_get '/var/backup/ohwr/db', LOCAL_DB_PATH
  end

  desc "Implements the redmine files backup"
  task :files => :prepare_folders do
    log "Backing up redmine files"
    rdiff_get '/var/rails/ohwr/shared/files', LOCAL_FILES_PATH
  end

  desc "Implements the sympa files backup"
  task :sympa => :prepare_folders do
    log "Backing up sympa files & config"
    rdiff_get '/home/sympa/arc', File.join(LOCAL_SYMPA_PATH,'arc')
    rdiff_get '/home/sympa/list_data', File.join(LOCAL_SYMPA_PATH,'list_data')
  end

  desc "Implements all backup (svn+db+files+sympa)"
  task :all => [:svn, :git, :db, :files, :sympa]

end
