backup revision 0978722f6ca27f5d5f5ed4ec8400703dfe211184
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa#!/usr/bin/env ruby
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Description
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# This backup script creates and restores backups of ontohub data. It includes:
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# * bare git repositories (data/repositories)
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# * named symlinks to git repositories (data/git_daemon)
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# * the postgres database
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa#
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Usage
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa# First note: Run this as the ontohub user, *not* as root.
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# To create a backup, run this script with the argument `create`:
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# $ script/backup create
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Then a backup named with the current date and time is created in the
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# backup directory (see below).
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa#
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# To restore a backup, run this script with the argument `restore <backup name>`
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# $ script/backup restore 2015-01-01_00-00
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Then the selected backup is fully restored
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa#
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Backup directory
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# For development machines, the backup directory is:
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# <rails root>/tmp/backup/
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# And for production machines, the backup directory is:
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# /home/ontohub/ontohub_data_backup
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa#
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Super user privileges
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# To create and restore, we need root privileges. Otherwise file modes are not
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# preserved. This script will call `sudo` when needed and inform you about the
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# reason for calling `sudo`. If you don't allow sudo, a backup will be created
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# or restored anyway, but the file modes and ownership are not preserved.
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Then, you need to adjust them manually.
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa#
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# Maintenance mode
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# While backing up and restoring the data, the maintenance mode is activated.
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa# This way we guarantee data consistency of the backup.
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa
f0264afd33a980b6584747fc8159ee950805d9e3Eugen Kuksa
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksarequire 'tmpdir.rb'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksarequire 'fileutils'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksarequire 'pathname'
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksarequire 'open3'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
b1fe9054ad7c7192fe4c474363247dad15963e99Eugen Kuksamodule Backup
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa class Backup
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa # Amount of backups that have to be there at least
d89f470f7da0b9f8295d0ac0defff09884894b8bEugen Kuksa BACKUPS_COUNT = 30
d89f470f7da0b9f8295d0ac0defff09884894b8bEugen Kuksa # Backups are kept for at least 365 days
d89f470f7da0b9f8295d0ac0defff09884894b8bEugen Kuksa BACKUPS_VALIDITY_TIME = 365 * 60 * 60 * 24
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa MAINTENANCE_FILE = 'maintenance.txt'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa REPOSITORY_FILE = 'ontohub_repositories.tar.gz'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa DATA_DIRS = %w(data/repositories data/git_daemon)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
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
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def initialize(db_name, data_root, backup_root,
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa verbose: false, dry_run: true, sql_dump_as_postgres_user: false)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @db_name = db_name
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @data_root = Pathname.new(data_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @backup_root = Pathname.new(backup_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @dry_run = dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @verbose = verbose
366ce8d807067a97613cb23d49105d8a093c5015Eugen Kuksa @sql_dump_as_postgres_user = sql_dump_as_postgres_user
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def create
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Creating backup...'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa enable_maintenance_mode
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa initialize_backup
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa create_sql_dump
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa create_repository_archive
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa # We needed to create the directory for the script to continue later on.
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Dir.rmdir(backup_instance_dir) if dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa disable_maintenance_mode
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts "Created backup in #{backup_instance_dir}"
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa self.class.prune(backup_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def restore(backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa enable_maintenance_mode
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa initialize_restore(backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa restore_sql_dump
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa restore_repository_archive
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa disable_maintenance_mode
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts "Restored backup from #{backup_instance_dir}"
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa def self.prune(backup_root)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa if !Dir.exists?(backup_root)
923d69139038e74c0936e826bbfdc8717fbbc7b3Eugen Kuksa $stderr.puts "Nothing to prune: There is no backup directory."
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa return
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa now = Time.now
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir|
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa backup = backup_root.join(dir)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa puts "removing old backup: #{dir}"
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa FileUtils.rm_r(backup)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa protected
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def new_backup_name
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Time.now.strftime("%Y-%m-%d_%H-%M-%S")
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def initialize_backup
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.
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa FileUtils.mkdir_p(backup_instance_dir)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def create_sql_dump
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Creating SQL dump...'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Dir.chdir(backup_instance_dir) do
b4ac7fd47818fbcba8d344e3de41ca62e1473b94Eugen Kuksa exec('pg_dump', *pg_user_switch, '-Fc', db_name,
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa file_dest: SQL_DUMP_FILE)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def create_repository_archive
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Creating repository archive...'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Dir.chdir(data_root.join('..')) do
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa archive_file = backup_instance_dir.join(REPOSITORY_FILE)
284432981d641cf3d679841f75acbcf039d83062Eugen Kuksa exec('tar', verbose ? '-v' : '', '-czf', archive_file.to_s,
284432981d641cf3d679841f75acbcf039d83062Eugen Kuksa *DATA_DIRS.map(&:to_s))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def initialize_restore(backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa @backup_instance_dir = backup_root.join(backup_name)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa unless Dir.exists?(backup_instance_dir)
4634cde5d3428bd5ab34b8212ac2f4637cdfff6fEugen Kuksa $stderr.puts (
4634cde5d3428bd5ab34b8212ac2f4637cdfff6fEugen Kuksa "Error: Backup '#{backup_name}' does not exist in #{backup_root}.")
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa exit
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def restore_sql_dump
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa 'Restoring SQL dump...'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Dir.chdir(backup_instance_dir) do
b4ac7fd47818fbcba8d344e3de41ca62e1473b94Eugen Kuksa exec('pg_restore', '-c', *pg_user_switch, '-d', db_name, SQL_DUMP_FILE)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def restore_repository_archive
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Restoring repository archive...'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa Dir.chdir(data_root.join('..')) do
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa tmpdir = Dir.mktmpdir
2dbc668d1e44c95db1857d3968bcde7517852beaEugen Kuksa move_data_dirs_to_tmpdir(tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa begin
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa extract_archive
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa remove_tmpdir(tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rescue => e
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts <<-MSG
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaAn error occured while restoring the repositories:
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa#{e.message}
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaYou can find the pre-restore repositories at #{tmpdir}
b51057b860560bf3ee454c03a121af3d5d34f482Eugen KuksaDo something about it.
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa MSG
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa raise e
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
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 Kuksa puts <<-MSG
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa
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 MSG
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa exec('sudo', 'mv', *DATA_DIRS.map(&:to_s), tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa def extract_archive
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa archive_file = backup_instance_dir.join(REPOSITORY_FILE)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts <<-MSG
284432981d641cf3d679841f75acbcf039d83062Eugen Kuksa
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
6cdc461aada609d57d50ff675d29b15378717ff2Eugen Kuksa(Ctl-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 MSG
284432981d641cf3d679841f75acbcf039d83062Eugen Kuksa try_as_sudo_with_fallback('tar', verbose ? '-v' : '', '-xzf',
284432981d641cf3d679841f75acbcf039d83062Eugen Kuksa archive_file.to_s, *DATA_DIRS.map(&:to_s))
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa
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 Kuksa puts <<-MSG
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa
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 MSG
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa exec('sudo', 'rm', '-r', tmpdir)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def enable_maintenance_mode
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Enabling maintenance mode...'
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa puts "FileUtils.touch #{maintenance_file}" if verbose
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa FileUtils.touch maintenance_file unless dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa def disable_maintenance_mode
4ec9d8b62c3c1a001548eb0883b6f81e00c391a0Eugen Kuksa puts 'Disabling maintenance mode...'
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa puts "FileUtils.rm #{maintenance_file}" if verbose
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa FileUtils.rm maintenance_file unless dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa def exec(*args, file_dest: nil)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa puts "[executing next command in #{Dir.getwd}]" if verbose
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa out = args.join(' ')
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa out << " > #{file_dest}" if file_dest
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa puts out if verbose
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa Subprocess.run(*args, file_dest: file_dest) unless dry_run
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa def try_as_sudo_with_fallback(*args)
dd553f2f8b8abb774ba64a4fb9ebe3abea9f7f17Eugen Kuksa _out, _err, exit_code = exec('sudo', *args)
dd553f2f8b8abb774ba64a4fb9ebe3abea9f7f17Eugen Kuksa unless exit_code.success?
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa sudo_not_given_fallback(*args) # Wrong sudo password
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa rescue Exception => e
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa raise e unless e.is_a?(Interrupt) # Ctrl-C when asked for password
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa sudo_not_given_fallback(*args)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa def sudo_not_given_fallback(*args)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa puts 'Super user privileges not granted. Trying as normal user.'
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa exec(*args)
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa end
b51057b860560bf3ee454c03a121af3d5d34f482Eugen Kuksa
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa def maintenance_file
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa data_root.join(MAINTENANCE_FILE)
486df98bbf3348cfb96e93c3e499d12435880bb5Eugen Kuksa end
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa def pg_user_switch
b4ac7fd47818fbcba8d344e3de41ca62e1473b94Eugen Kuksa sql_dump_as_postgres_user ? %w(-U postgres) : []
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa end
6d055d16c7620b7804b6a46cb481d00b3dbb5007Eugen Kuksa
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa def self.backup_dirs_allowed_to_delete(entries)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)]
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa class Subprocess
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa def self.run(*args, file_dest: nil)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa stdin, stdout, stderr, wait_thr, io_dest = run_streaming(*args,
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa file_dest: file_dest)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa exit_code = wait_thr.value # wait for the process to finish
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa io_dest.close if io_dest
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa [stdout.read, stderr.read, exit_code]
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa end
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa def self.run_streaming(*args, file_dest: nil)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa stdin, stdout, stderr, wait_thr = Open3.popen3(*args)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa io_dest = nil
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa if file_dest
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa io_dest = File.open(file_dest, 'w')
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa IO.copy_stream(stdout, io_dest)
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa end
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa [stdin, stdout, stderr, wait_thr, io_dest]
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa end
28001d576e67ba46ed481c5695f1e0827ff26007Eugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaend
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksadef data_root(rails_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa File.realpath(rails_root.join('data'))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaend
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksadef on_development_system?(rails_root)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa !File.symlink?(rails_root.join('data'))
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaend
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa# Don't allow this to be run as the root user.
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksaif ENV['USER'] == 'root'
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa puts 'Running this script as the root user is disabled.'
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa puts 'Please run it as a normal user that has sudo privileges, e.g. ontohub.'
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa exit
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksaend
0978722f6ca27f5d5f5ed4ec8400703dfe211184Eugen Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa# We assume, this script runs in "RAILS_ROOT/script/".
daf3e28fff47a65b53d6fb65155301763b9f166eEugen KuksaRAILS_ROOT = Pathname.new(__FILE__).dirname.join('..')
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen KuksaBACKUP_ROOT_PRODUCTION = '/home/ontohub/ontohub_data_backup'
0b99be5ced371cca00283694e1bd53a8ac0d7b5dEugen Kuksa
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen KuksaDATABASE =
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen Kuksa if on_development_system?(RAILS_ROOT)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa 'ontohub_development'
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa else
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa 'ontohub'
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa end
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen Kuksa
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen KuksaBACKUP_ROOT =
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen Kuksa if on_development_system?(RAILS_ROOT)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa RAILS_ROOT.join('tmp', 'backup')
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa else
1724834ae596f84e8237a3bba5a8af15916a8ee9Eugen Kuksa File.realpath(BACKUP_ROOT_PRODUCTION)
df4ff7ec6be98e7ae5830731becc1a3d55105378Eugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa
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 Kuksa
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksacase ARGV.first
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksawhen 'create'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup.create
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksawhen 'restore'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa if ARGV.length == 1
4634cde5d3428bd5ab34b8212ac2f4637cdfff6fEugen Kuksa $stderr.puts(
4634cde5d3428bd5ab34b8212ac2f4637cdfff6fEugen Kuksa 'To restore a backup, you need to specify one with the arguments')
4634cde5d3428bd5ab34b8212ac2f4637cdfff6fEugen Kuksa $stderr.puts('"restore backup_name"')
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa exit
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa end
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup_name = ARGV[1]
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa backup.restore(backup_name)
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksawhen 'prune'
5efadb4662f2a63d5f5f1a5b303ab7c3371069a8Eugen Kuksa Backup::Backup.prune(BACKUP_ROOT)
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaelse
923d69139038e74c0936e826bbfdc8717fbbc7b3Eugen Kuksa $stderr.puts 'unknown or missing parameter'
923d69139038e74c0936e826bbfdc8717fbbc7b3Eugen Kuksa $stderr.puts 'use parameter "create" or "restore <backup_name>" or "prune"'
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksa exit
daf3e28fff47a65b53d6fb65155301763b9f166eEugen Kuksaend