backup revision a9b1a876554e3539bd8e70e6081888c8a0134f22
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#!/local/usr/ruby/shims/ruby
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# The first line is for deployment machines only. For local machines, use:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#!/usr/bin/env ruby
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You can find more extensive documentation of this script at
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# https://github.com/ontohub/ontohub/blob/staging/doc/backup_and_restore_of_ontohub_data.md
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Description
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# This backup script creates and restores backups of ontohub data. It includes:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# * bare git repositories (data/repositories)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# * named symlinks to git repositories (data/git_daemon)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# * the postgres database
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Usage
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# First note: Run this as the root user, e.g. with sudo.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# To create a backup, run this script with the argument `create`:
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# # script/backup create
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Then a backup named with the current date and time is created in the
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# backup directory (see below).
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen#
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# To restore a backup, run this script with the argument `restore <backup name>`
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# # script/backup restore 2015-01-01_00-00
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Then the selected backup is fully restored
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen#
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Backup directory
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# For development machines, the backup directory is:
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# <rails root>/tmp/backup/
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# And for production machines, the backup directory is:
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# /home/ontohub/ontohub_data_backup
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen#
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Super user privileges
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# To create and restore, we need root privileges. Otherwise file modes are not
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# preserved. This script will call `sudo` when needed and inform you about the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# reason for calling `sudo`. If you don't allow sudo, a backup will be created
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen# or restored anyway, but the file modes and ownership are not preserved.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Then, you need to adjust them manually.
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen#
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Maintenance mode
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# While backing up and restoring the data, the maintenance mode is activated.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# This way we guarantee data consistency of the backup.
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainenrequire 'tmpdir.rb'
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenrequire 'fileutils'
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenrequire 'pathname'
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainenrequire 'open3'
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenmodule Backup
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen class Backup
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen # Amount of backups that have to be there at least
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen BACKUPS_COUNT = 30
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # Backups are kept for at least 365 days
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen BACKUPS_VALIDITY_TIME = 365 * 60 * 60 * 24
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen MAINTENANCE_FILE = 'maintenance.txt'
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen SQL_DUMP_FILE = 'ontohub_sql_dump.postgresql'
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen REPOSITORY_FILE = 'ontohub_repositories.tar.gz'
f7d43647acc6dc80064c8c4cacf5bf86f754c530Timo Sirainen
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen DATA_DIRS = %w(repositories git_daemon)
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen # Use 'sudo' on most systems
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen SUDO_BINARY = '+'
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen attr_reader :db_name, :data_root, :backup_root, :backup_instance_dir
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen attr_reader :dry_run, :verbose, :sql_dump_as_db_user
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen def initialize(db_name, data_root, backup_root,
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen verbose: false, dry_run: true, sql_dump_as_db_user: nil,
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen user: nil, group: nil)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen @db_name = db_name
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen @backup_root = Pathname.new(backup_root)
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen @data_root = Pathname.new(data_root)
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen @data_root_basename = @data_root.basename.to_s
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen @data_dirs = DATA_DIRS.map { |dir| File.join(@data_root_basename, dir) }
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen @user = user
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen @group = group
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen @dry_run = dry_run
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen @verbose = verbose
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen @sql_dump_as_db_user = sql_dump_as_db_user
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen end
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen def create
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen puts 'Creating backup...'
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen enable_maintenance_mode
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen initialize_backup
445f9e31c6c3aa6c0a72be8565da8f6e594d24fbTimo Sirainen create_sql_dump
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen create_repository_archive
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen # We needed to create the directory for the script to continue later on.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen Dir.rmdir(backup_instance_dir) if dry_run
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen disable_maintenance_mode
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen puts "Created backup in #{backup_instance_dir}"
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen self.class.prune(backup_root)
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen end
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen def restore(backup_name)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen enable_maintenance_mode
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen initialize_restore(backup_name)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen restore_sql_dump
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen restore_repository_archive
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen disable_maintenance_mode
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts "Restored backup from #{backup_instance_dir}"
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def self.prune(backup_root)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if !Dir.exists?(backup_root)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen $stderr.puts "Nothing to prune: There is no backup directory."
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen end
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen now = Time.now
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen backup_dirs_allowed_to_delete(Dir.new(backup_root).entries).each do |dir|
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen backup = backup_root.join(dir)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if now - File.new(backup).ctime > BACKUPS_VALIDITY_TIME
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen puts "removing old backup: #{dir}"
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen FileUtils.rm_r(backup)
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen end
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen end
c3412ddeb9abc13f99d3caf50faf76cd99f7e9d2Timo Sirainen end
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen protected
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen def new_backup_name
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen Time.now.strftime("%Y-%m-%d_%H-%M-%S")
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen end
d0d7fcf3ce44f26fdf34c1542a25cec644c5c4c7Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen def initialize_backup
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen @backup_instance_dir = backup_root.join(new_backup_name)
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen puts "FileUtils.mkdir_p #{backup_instance_dir}" if verbose
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # Create directory even in dry run to let the script continue.
e8a35266a5ceacdfafeeffd6bddae77931ff97ebTimo Sirainen FileUtils.mkdir_p(backup_instance_dir)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen puts "FileUtils.chown #{@user} #{@group} #{backup_instance_dir}" if verbose
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen FileUtils.chown(@user, @group, backup_instance_dir)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen def create_sql_dump
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen puts 'Creating SQL dump...'
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen Dir.chdir(backup_instance_dir) do
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen exec('pg_dump', *pg_user_switch, '-Fc', db_name,
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen '-f', backup_instance_dir.join(SQL_DUMP_FILE), user: @user)
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen end
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen end
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen def create_repository_archive
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen puts 'Creating repository archive...'
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen Dir.chdir(data_root.join('..')) do
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen archive_file = backup_instance_dir.join(REPOSITORY_FILE)
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen exec('tar', verbose ? '-v' : '', '-cf', archive_file.to_s, *@data_dirs,
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen user: @user)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def initialize_restore(backup_name)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen @backup_instance_dir = backup_root.join(backup_name)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen unless Dir.exists?(backup_instance_dir)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen $stderr.puts (
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "Error: Backup '#{backup_name}' does not exist in #{backup_root}.")
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen exit
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen def restore_sql_dump
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen 'Restoring SQL dump...'
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen Dir.chdir(backup_instance_dir) do
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen exec('pg_restore', '-n', 'public',
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen '-c', *pg_user_switch,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen '-d', db_name,
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen SQL_DUMP_FILE,
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen user: @user)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen end
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen end
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def restore_repository_archive
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts 'Restoring repository archive...'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen Dir.chdir(data_root.join('..')) do
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen tmpdir = Dir.mktmpdir
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen move_data_dirs_to_tmpdir(tmpdir)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen begin
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen extract_archive
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen remove_tmpdir(tmpdir)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen rescue => e
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen puts <<-MSG
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenAn error occured while restoring the repositories:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen#{e.message}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenYou can find the pre-restore repositories at #{tmpdir}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenDo something about it.
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen MSG
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen raise e
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen end
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen end
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen end
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def move_data_dirs_to_tmpdir(tmpdir)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts "FileUtils.mv(#{@data_dirs}, #{tmpdir})" if verbose
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen FileUtils.mv(@data_dirs, tmpdir) unless dry_run
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen rescue Errno::EACCES
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen puts <<-MSG
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo SirainenAs the current user I have no access to move the repository data
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainendirectories #{@data_dirs.join(' ')} to a temporary directory #{tmpdir}.
bb10ebcf076c959c752f583746d83805d7686df8Timo SirainenThis is used as a backup for the case of an error while restoring.
bb10ebcf076c959c752f583746d83805d7686df8Timo SirainenTo continue, I try the command again using sudo.
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen MSG
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen exec('mv', *@data_dirs, tmpdir, user: 'root')
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen end
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen def extract_archive
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen archive_file = backup_instance_dir.join(REPOSITORY_FILE)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen puts <<-MSG
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
39775ad03c459efe64cce924658da5094ba417e1Timo SirainenSuper user privileges are needed to reset the file permissions as
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenthey were before the backup. If you refuse to enter the password
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen(Ctl-C) or enter a wrong password, only the permissions will not be
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenrestored and all restored files will belong to the current user/group.
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen MSG
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen exec('tar', verbose ? 'vxf' : 'xf', archive_file.to_s, *@data_dirs,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen user: 'root')
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen end
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen def remove_tmpdir(tmpdir)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen puts "FileUtils.remove_entry(#{tmpdir})" if verbose
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen FileUtils.remove_entry(tmpdir) # even do this in dry run
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen rescue Errno::EACCES
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen puts <<-MSG
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo SirainenAs the current user I have no access to remove the temporary
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainendirectory #{tmpdir}.
bb10ebcf076c959c752f583746d83805d7686df8Timo SirainenTo continue, I try the command again using sudo.
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen MSG
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen exec('rm', '-r', tmpdir, user: 'root')
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen end
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen def enable_maintenance_mode
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen puts 'Enabling maintenance mode...'
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if File.exist?(maintenance_file)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts 'Maintenance mode was already enabled.'
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts "Please check the file #{maintenance_file}"
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen $stderr.puts 'Aborting.'
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen exit
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts "FileUtils.touch #{maintenance_file}" if verbose
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen FileUtils.touch maintenance_file unless dry_run
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def disable_maintenance_mode
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts 'Disabling maintenance mode...'
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen puts "FileUtils.rm #{maintenance_file}" if verbose
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen FileUtils.rm maintenance_file unless dry_run
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen end
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen # Execute a command as the given user.
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen def exec(*args, user: nil)
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen print "[executing next command in #{Dir.getwd}" if verbose
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen print " as user #{user}" if verbose && user
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen puts "]" if verbose
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen out = args.join(' ')
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen puts out if verbose
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if !dry_run
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if user == 'root'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen exec_system(*[sudo, *args])
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen elsif user
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # This looks strange because of the double sudo.
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen # It is needed on our deployment machines to get the environment right.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen exec_system(*['+', 'sudo', '-u', user, 'bash', '-c',
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "cd #{Dir.getwd} && #{escape_arguments(args)}"])
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen else
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen exec_system(*args)
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen end
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen end
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen end
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen def sudo
2ae575a66f2a302f047f6de062a70b75f8bebc7bTimo Sirainen SUDO_BINARY
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen end
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen def exec_system(*args)
7f773564b94e6054a40d3785cb63c29f1e4d4deeTimo Sirainen # puts args.join(' ') # For debugging
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen system(*args)
484efa22e65c509f787dbbc892351146c726c257Timo Sirainen end
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen def escape_arguments(args)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen rest = args[1..-1].map do |arg|
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if arg.to_s.include?(' ')
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen %("#{arg.gsub('"', '\"')}")
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen else
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen arg
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen ([args[0]] + rest).join(' ')
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen def maintenance_file
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen data_root.join(MAINTENANCE_FILE)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen def pg_user_switch
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen sql_dump_as_db_user ? %W(-U #{sql_dump_as_db_user}) : []
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen def self.backup_dirs_allowed_to_delete(entries)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen entries.reject{ |entry| %w(. ..).include?(entry) }[0..-(BACKUPS_COUNT+1)]
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen end
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenend
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainendef data_root(rails_root)
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen if on_development_system?(rails_root)
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen File.realpath(rails_root.join('data'))
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen else
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen ENV['DATA_ROOT'] ||'/data/git'
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen end
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenend
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainendef on_development_system?(rails_root)
f0f9c8e94abac18f8acd91b9e724c4c32863723aTimo Sirainen data_path = rails_root.join('data')
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen File.exist?(data_path) && !File.symlink?(data_path)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenend
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen# Don't allow this to be run as the root user.
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenif ENV['USER'] != 'root'
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen puts 'Running this script as a normal user is disabled.'
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen puts 'Please run it as root.'
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen exit
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenend
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen# We assume, this script runs in "RAILS_ROOT/script/".
7f3be7d885c75cdd77f536929a45bc9764595960Timo SirainenRAILS_ROOT = Pathname.new(__FILE__).dirname.join('..')
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenBACKUP_ROOT_PRODUCTION = '/local/home/ontohub/ontohub_data_backup'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenUSER = 'ontohub'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo SirainenGROUP = 'webservd'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
4d25408732be27e91f0430f71e87242760c2517cTimo SirainenDATABASE =
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if on_development_system?(RAILS_ROOT)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'ontohub_development'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen else
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen 'ontohub'
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen end
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo SirainenBACKUP_ROOT =
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen if on_development_system?(RAILS_ROOT)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen RAILS_ROOT.join('tmp', 'backup')
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen else
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen File.realpath(BACKUP_ROOT_PRODUCTION)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen end
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainenbackup = Backup::Backup.new(DATABASE, data_root(RAILS_ROOT), BACKUP_ROOT,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_dump_as_db_user: on_development_system?(RAILS_ROOT) ? 'postgres' : 'ontohub',
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen user: USER, group: GROUP,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen dry_run: false, verbose: true)
8af07808ba203f8709e2ff9eaf2291e1c4a4d53dTimo Sirainen
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainencase ARGV.first
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenwhen 'create'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen backup.create
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenwhen 'restore'
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if ARGV.length == 1
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts(
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen 'To restore a backup, you need to specify one with the arguments')
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts('"restore backup_name"')
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen exit
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen end
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen backup_name = ARGV[1]
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen backup.restore(backup_name)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenwhen 'prune'
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen Backup::Backup.prune(BACKUP_ROOT)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenelse
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts 'unknown or missing parameter'
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen $stderr.puts 'use parameter "create" or "restore <backup_name>" or "prune"'
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen exit
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenend
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen