backup revision 5efadb4662f2a63d5f5f1a5b303ab7c3371069a8
1057N/A#!/usr/bin/env ruby
1057N/A
1057N/Arequire 'fileutils'
1057N/Arequire 'pathname'
1589N/A
1057N/Amodule Backup
660N/A class Backup
1057N/A # Amount of backups that have to be there at least
1057N/A BACKUPS_COUNT = 3
1057N/A # Backups are valid for 7 days
1057N/A BACKUPS_VALIDITY_TIME = 7 * 60 * 60 * 24
1057N/A
1057N/A MAINTENANCE_FILE = 'maintenance.txt'
1057N/A
1057N/A SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql'
1057N/A REPOSITORY_FILE = 'ontohub_repositories.tar.gz'
1057N/A
1057N/A DATA_DIRS = %w(data/repositories data/git_daemon)
1057N/A
1057N/A attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir
1703N/A attr_reader :dry_run, :verbose, :sql_dump_without_su
1057N/A
660N/A def initialize(db_name, data_root, backup_root,
1109N/A verbose: false, dry_run: true, sql_dump_without_su: false)
1109N/A @db_name = db_name
660N/A @data_root = Pathname.new(data_root)
1703N/A @backup_root = Pathname.new(backup_root)
1703N/A
1589N/A @dry_run = dry_run
1703N/A @verbose = verbose
1741N/A @sql_dump_without_su = sql_dump_without_su
1741N/A end
1741N/A
1589N/A def create
1589N/A enable_maintenance_mode
1589N/A initialize_backup
1741N/A create_sql_dump
1589N/A create_repository_archive
1589N/A # We needed to create the directory for the script to continue later on.
1703N/A Dir.rmdir(backup_instance_dir) if dry_run
1589N/A disable_maintenance_mode
1741N/A puts "created backup in #{backup_instance_dir}"
1589N/A self.class.prune(backup_root)
1703N/A end
684N/A
684N/A def restore(backup_name)
684N/A enable_maintenance_mode
684N/A initialize_restore(backup_name)
684N/A restore_sql_dump
684N/A restore_repository_archive
684N/A disable_maintenance_mode
1703N/A puts "restored backup from #{backup_instance_dir}"
1057N/A end
1057N/A
1057N/A def self.prune(backup_root)
684N/A if !Dir.exists?(backup_root)
684N/A puts "Nothing to prune: There is no backup directory."
684N/A return
684N/A end
1109N/A now = Time.now
684N/A backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir|
684N/A backup = backup_root.join(dir)
1703N/A if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME
1703N/A puts "removing old backup: #{dir}"
684N/A FileUtils.rm_r(backup)
684N/A end
1589N/A end
1703N/A end
1703N/A
1057N/A protected
1589N/A
1589N/A def new_backup_name
1589N/A Time.now.strftime("%Y-%m-%d_%H-%M-%S")
1703N/A end
1703N/A
1703N/A def initialize_backup
1703N/A @backup_instance_dir = backup_root.join(new_backup_name)
684N/A puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose
684N/A # Create directory even in dry run to let the script continue.
684N/A FileUtils.mkdir_p(backup_instance_dir)
684N/A end
1057N/A
1703N/A def create_sql_dump
1703N/A Dir.chdir(backup_instance_dir) do
1703N/A if sql_dump_without_su
684N/A exec "pg_dump -U postgres -Fc #{db_name} > #{SQL_DUMP_FILE}"
1703N/A else
1703N/A exec "sudo -u postgres pg_dump -Fc #{db_name} > #{SQL_DUMP_FILE}"
1703N/A end
1057N/A end
1703N/A end
684N/A
684N/A def create_repository_archive
684N/A Dir.chdir(data_root.join('..')) do
684N/A archive_file = backup_instance_dir.join(REPOSITORY_FILE)
684N/A exec(["tar -czvf #{archive_file}", *DATA_DIRS].join(' '))
684N/A end
1057N/A end
684N/A
684N/A def initialize_restore(backup_name)
684N/A @backup_instance_dir = backup_root.join(backup_name)
1703N/A unless Dir.exists?(backup_instance_dir)
1703N/A puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}."
1703N/A exit
1703N/A end
1703N/A end
1703N/A
1703N/A def restore_sql_dump
1703N/A Dir.chdir(backup_instance_dir) do
1703N/A if sql_dump_without_su
1703N/A exec "pg_restore -c -U postgres -d #{db_name} #{SQL_DUMP_FILE}"
1703N/A else
1703N/A exec "sudo -u postgres pg_restore -c -d #{db_name} #{SQL_DUMP_FILE}"
1703N/A end
1703N/A end
1703N/A end
1703N/A
1703N/A def restore_repository_archive
1703N/A Dir.chdir(data_root.join('..')) do
1703N/A puts "FileUtils.rm_r #{DATA_DIRS}" if verbose
1703N/A FileUtils.rm_r DATA_DIRS unless dry_run
1703N/A archive_file = backup_instance_dir.join(REPOSITORY_FILE)
1703N/A exec(["tar -xzvf #{archive_file}", *DATA_DIRS].join(' '))
1703N/A end
1703N/A end
1703N/A
1703N/A def enable_maintenance_mode
1703N/A puts "FileUtils.touch #{maintenance_file}" if verbose
1703N/A FileUtils.touch maintenance_file unless dry_run
1703N/A end
1703N/A
1703N/A def disable_maintenance_mode
1703N/A puts "FileUtils.rm #{maintenance_file}" if verbose
1703N/A FileUtils.rm maintenance_file unless dry_run
1703N/A end
1703N/A
1703N/A def exec(command)
1703N/A puts "[executing next command in #{Dir.getwd}]" if verbose
1703N/A puts command if verbose
1703N/A system(command) unless dry_run
1703N/A end
1703N/A
1703N/A def maintenance_file
1703N/A data_root.join(MAINTENANCE_FILE)
1703N/A end
1703N/A
1703N/A def self.backup_dirs_allowed_to_delete(entries)
1703N/A entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)]
1703N/A end
1703N/A end
1703N/Aend
1703N/A
1703N/Adef data_root(rails_root)
1741N/A File.realpath(rails_root.join('data'))
1703N/Aend
1703N/A
1703N/Adef on_development_system?(rails_root)
1703N/A !File.symlink?(rails_root.join('data'))
1703N/Aend
1703N/A
1703N/A# We assume, this script runs in "RAILS_ROOT/script/".
1703N/ARAILS_ROOT = Pathname.new(__FILE__).dirname.join('..')
1703N/A
1703N/ADATABASE = on_development_system?(RAILS_ROOT) ? 'ontohub_development' : 'ontohub'
1703N/ABACKUP_ROOT = File.realpath('/home/ontohub/ontohub_data_backup')
1703N/A
1703N/Abackup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT,
1703N/A sql_dump_without_su: on_development_system?(RAILS_ROOT),
684N/A dry_run: false, verbose: true)
684N/A
684N/Acase ARGV.first
1703N/Awhen 'create'
1589N/A backup.create
1589N/Awhen 'restore'
1589N/A if ARGV.length == 1
1703N/A puts 'To restore a backup, you need to specify one with the arguments'
1703N/A puts '"restore backup_name"'
1703N/A exit
1703N/A end
1703N/A backup_name = ARGV[1]
684N/A backup.restore(backup_name)
684N/Awhen 'prune'
684N/A Backup::Backup.prune(BACKUP_ROOT)
1703N/Aelse
1703N/A puts 'unknown or missing parameter'
1741N/A puts 'use parameter "create" or "restore <backup_name>" or "prune"'
1703N/A exit
1703N/Aend
1703N/A