backup revision 706a446ea5c5c1644b53a82fcadd8c9080ba3b4f
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# The first line is for deployment machines only. For local machines, use:
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# You can find more extensive documentation of this script at
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# https://github.com/ontohub/ontohub/blob/staging/doc/backup_and_restore_of_ontohub_data.md
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# This backup script creates and restores backups of ontohub data. It includes:
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# * bare git repositories (data/repositories)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# * named symlinks to git repositories (data/git_daemon and data/git_ssh)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# * the postgres database
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# First note: Run this as the root user, e.g. with sudo.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# To create a backup, run this script with the argument `create`:
9cd39d7306a472544733aff760d2916888d2b1f4David Lawrence# Then a backup named with the current date and time is created in the
9cd39d7306a472544733aff760d2916888d2b1f4David Lawrence# backup directory (see below).
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# To restore a backup, run this script with the argument `restore <backup name>`
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# # script/backup restore 2015-01-01_00-00
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# Then the selected backup is fully restored
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# Backup directory
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# For production machines, the backup directory is:
5273184ae1ae4fbb30c54d59c6c40ab2c68312afMark Andrews# Super user privileges
5273184ae1ae4fbb30c54d59c6c40ab2c68312afMark Andrews# To create and restore, we need root privileges. Otherwise file modes are not
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# preserved. This script will call `sudo` when needed and inform you about the
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# reason for calling `sudo`. If you don't allow sudo, a backup will be created
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# or restored anyway, but the file modes and ownership are not preserved.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# Then, you need to adjust them manually.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# Maintenance mode
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# While backing up and restoring the data, the maintenance mode is activated.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence# This way we guarantee data consistency of the backup.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrencerequire 'fileutils'
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrencerequire 'pathname'
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrencerequire 'open3'
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence # Amount of backups that have to be there at least
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence # Backups are kept for at least 365 days
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql'
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence REPOSITORY_FILE = 'ontohub_repositories.tar.gz'
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence DATA_DIRS = %w(repositories git_daemon git_ssh)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence # Use 'sudo' on most systems
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence attr_reader :dry_run, :verbose, :sql_dump_as_db_user
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence def initialize(db_name, data_root, backup_root,
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence verbose: false, dry_run: true, sql_dump_as_db_user: nil,
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence @data_root_basename = @data_root.basename.to_s
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence @data_dirs = DATA_DIRS.map { |dir| File.join(@data_root_basename, dir) }
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence # We needed to create the directory for the script to continue later on.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence puts "Created backup in #{backup_instance_dir}"
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence puts "Restored backup from #{backup_instance_dir}"
a890fbefa3a143ff0513854c895e0f04c8d72bd5David Lawrence $stderr.puts "Nothing to prune: There is no backup directory."
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir|
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence @backup_instance_dir = backup_root.join(new_backup_name)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence # Create directory even in dry run to let the script continue.
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence puts "FileUtils.chown #{@user} #{@group} #{backup_instance_dir}" if verbose
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence FileUtils.chown(@user, @group, backup_instance_dir)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence exec('pg_dump', *pg_user_switch, '-Fc', db_name,
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence '-f', backup_instance_dir.join(SQL_DUMP_FILE), user: @user)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence archive_file = backup_instance_dir.join(REPOSITORY_FILE)
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *@data_dirs,
8b7304a34c751e519ede7d00b77f1f962c0a37e4David Lawrence @backup_instance_dir = backup_root.join(backup_name)
996f4a8bc34cb0203ce6a40ff82bca8bf32423ccAndreas Gustafsson "Error: Backup '#{backup_name}' does not exist in #{backup_root}.")
rescue => e
puts <<-MSG