backup revision b51057b860560bf3ee454c03a121af3d5d34f482
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksarequire 'fileutils'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksarequire 'pathname'
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksarequire 'pry'
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksarequire 'pry-byebug'
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa # Amount of backups that have to be there at least
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa # Backups are valid for 7 days
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa DATA_DIRS = %w(data/repositories data/git_daemon)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa attr_reader :dry_run, :verbose, :sql_dump_as_postgres_user
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa verbose: false, dry_run: true, sql_dump_as_postgres_user: false)
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa @sql_dump_as_postgres_user = sql_dump_as_postgres_user
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa # We needed to create the directory for the script to continue later on.
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts "restored backup from #{backup_instance_dir}"
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa puts "Nothing to prune: There is no backup directory."
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir|
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @backup_instance_dir = backup_root.join(new_backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa # Create directory even in dry run to let the script continue.
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa exec "pg_dump#{pg_user_switch} -Fc #{db_name} > #{SQL_DUMP_FILE}"
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa archive_file = backup_instance_dir.join(REPOSITORY_FILE)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa exec(["tar -czvf #{archive_file}", *DATA_DIRS].join(' '))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @backup_instance_dir = backup_root.join(backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts "Error: Backup '#{backup_name}' does not exist in #{backup_root}."
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa exec "pg_restore -c#{pg_user_switch} -d #{db_name} #{SQL_DUMP_FILE}"
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaAn error occured while restoring the repositories:
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaYou can find the pre-restore repositories at #{tmpdir}
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaDo something about it.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def move_data_dirs_to_tmpdir(tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts "FileUtils.mv(#{DATA_DIRS}, #{tmpdir})" if verbose
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa FileUtils.mv(DATA_DIRS, tmpdir) unless dry_run
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rescue Errno::EACCES
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaAs the current user I have no access to move the repository data
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksadirectories #{DATA_DIRS.join(' ')} to a temporary directory #{tmpdir}.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaThis is used as a backup for the case of an error while restoring.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaTo continue, I try the command again using sudo.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa exec('sudo', 'mv', *DATA_DIRS.map(&:to_s), tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def extract_archive
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa archive_file = backup_instance_dir.join(REPOSITORY_FILE)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaSuper user privileges are needed to reset the file permissions as
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksathey were before the backup. If you refuse to enter the password
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa(Crtl-C) or enter a wrong password, only the permissions will not be
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksarestored and all restored files will belong to the current user/group.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa try_as_sudo_with_fallback('tar', '-xzvf', archive_file.to_s,
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa *DATA_DIRS.map(&:to_s))
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def remove_tmpdir(tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts "FileUtils.remove_entry(#{tmpdir})" if verbose
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa FileUtils.remove_entry(tmpdir) # even do this in dry run
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rescue Errno::EACCES
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaAs the current user I have no access to remove the temporary
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksadirectory #{tmpdir}.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaTo continue, I try the command again using sudo.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa exec('sudo', 'rm', '-r', tmpdir)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def enable_maintenance_mode
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa puts "FileUtils.touch #{maintenance_file}" if verbose
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa FileUtils.touch maintenance_file unless dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def disable_maintenance_mode
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa puts "FileUtils.rm #{maintenance_file}" if verbose
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa FileUtils.rm maintenance_file unless dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def exec(command)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts "[executing next command in #{Dir.getwd}]" if verbose
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts command if verbose
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa system(command) unless dry_run
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def try_as_sudo_with_fallback(command)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa unless exec("sudo #{command}")
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa # Wrong sudo password
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa sudo_not_given_fallback(command)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rescue Exception => e
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa # Ctrl-C when asked for password
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa raise e unless e.is_a?(Interrupt)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa sudo_not_given_fallback(command)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def sudo_not_given_fallback(command)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts 'Super user privileges not granted. Trying as normal user.'
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa exec(command)
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa def maintenance_file
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa data_root.join(MAINTENANCE_FILE)
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa def pg_user_switch
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa sql_dump_as_postgres_user ? ' -U postgres' : ''
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa def self.backup_dirs_allowed_to_delete(entries)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)]
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksadef data_root(rails_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa File.realpath(rails_root.join('data'))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksadef on_development_system?(rails_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa !File.symlink?(rails_root.join('data'))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa# We assume, this script runs in "RAILS_ROOT/script/".
daf3e28fff47a65b53d6fb65155301763b9f166eEugen KuksaRAILS_ROOT = Pathname.new(__FILE__).dirname.join('..')
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen KuksaDATABASE = if on_development_system?(RAILS_ROOT)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa 'ontohub_development'
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen KuksaBACKUP_ROOT = if on_development_system?(RAILS_ROOT)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa RAILS_ROOT.join('tmp', 'backup')
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa File.realpath('/home/ontohub/ontohub_data_backup')
b1fe9054ad7c7192fe4c474363247dad15963e99Eugen Kuksabackup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT,
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa sql_dump_as_postgres_user: on_development_system?(RAILS_ROOT),
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa dry_run: false, verbose: true)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksacase ARGV.first
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksawhen 'create'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup.create
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksawhen 'restore'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa if ARGV.length == 1
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts 'To restore a backup, you need to specify one with the arguments'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts '"restore backup_name"'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup_name = ARGV[1]
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup.restore(backup_name)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa Backup::Backup.prune(BACKUP_ROOT)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts 'unknown or missing parameter'
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa puts 'use parameter "create" or "restore <backup_name>" or "prune"'