refactor backup/restore

This commit is contained in:
Dmitriy Zaporozhets 2013-04-05 19:01:19 +03:00
parent 362d82d10e
commit c33d5e16fe
5 changed files with 140 additions and 103 deletions

View file

@ -21,7 +21,7 @@ GitLab supports the following databases:
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
# Grant the GitLab user necessary permissopns on the table.
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost';
mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost';
# Quit the database session
mysql> \q

View file

@ -1,56 +0,0 @@
require 'yaml'
class Backup
attr_reader :config, :db_dir
def initialize
@config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
@db_dir = File.join(Gitlab.config.backup.path, 'db')
FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir)
end
def backup_db
case config["adapter"]
when /^mysql/ then
system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}")
when "postgresql" then
pg_env
system("pg_dump #{config['database']} > #{db_file_name}")
end
end
def restore_db
case config["adapter"]
when /^mysql/ then
system("mysql #{mysql_args} #{config['database']} < #{db_file_name}")
when "postgresql" then
pg_env
system("pg_restore #{config['database']} #{db_file_name}")
end
end
protected
def db_file_name
File.join(db_dir, 'database.sql')
end
def mysql_args
args = {
'host' => '--host',
'port' => '--port',
'socket' => '--socket',
'username' => '--user',
'encoding' => '--default-character-set',
'password' => '--password'
}
args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact.join(' ')
end
def pg_env
ENV['PGUSER'] = config["username"] if config["username"]
ENV['PGHOST'] = config["host"] if config["host"]
ENV['PGPORT'] = config["port"].to_s if config["port"]
ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
end
end

58
lib/backup/database.rb Normal file
View file

@ -0,0 +1,58 @@
require 'yaml'
module Backup
class Database
attr_reader :config, :db_dir
def initialize
@config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
@db_dir = File.join(Gitlab.config.backup.path, 'db')
FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir)
end
def dump
case config["adapter"]
when /^mysql/ then
system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}")
when "postgresql" then
pg_env
system("pg_dump #{config['database']} > #{db_file_name}")
end
end
def restore
case config["adapter"]
when /^mysql/ then
system("mysql #{mysql_args} #{config['database']} < #{db_file_name}")
when "postgresql" then
pg_env
system("pg_restore #{config['database']} #{db_file_name}")
end
end
protected
def db_file_name
File.join(db_dir, 'database.sql')
end
def mysql_args
args = {
'host' => '--host',
'port' => '--port',
'socket' => '--socket',
'username' => '--user',
'encoding' => '--default-character-set',
'password' => '--password'
}
args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact.join(' ')
end
def pg_env
ENV['PGUSER'] = config["username"] if config["username"]
ENV['PGHOST'] = config["host"] if config["host"]
ENV['PGPORT'] = config["port"].to_s if config["port"]
ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
end
end
end

74
lib/backup/repository.rb Normal file
View file

@ -0,0 +1,74 @@
require 'yaml'
module Backup
class Repository
attr_reader :repos_path
def dump
prepare
Project.find_each(batch_size: 1000) do |project|
print " * #{project.path_with_namespace} ... "
if project.empty_repo?
puts "[SKIPPED]".cyan
next
end
# Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if system("cd #{path_to_repo(project)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(project)} --all > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
end
def restore
if File.exists?(repos_path)
# Move repos dir to 'repositories.old' dir
bk_repos_path = File.join(repos_path, '..', 'repositories.old')
FileUtils.mv(repos_path, bk_repos_path)
end
FileUtils.mkdir_p(repos_path)
Project.find_each(batch_size: 1000) do |project|
print "#{project.path_with_namespace} ... "
project.namespace.ensure_dir_exist if project.namespace
if system("git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)} > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
end
protected
def path_to_repo(project)
File.join(repos_path, project.path_with_namespace + '.git')
end
def path_to_bundle(project)
File.join(backup_repos_path, project.path_with_namespace + ".bundle")
end
def repos_path
Gitlab.config.gitlab_shell.repos_path
end
def backup_repos_path
File.join(Gitlab.config.backup.path, "repositories")
end
def prepare
FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(backup_repos_path)
end
end
end

View file

@ -111,67 +111,28 @@ namespace :gitlab do
namespace :repo do
task :create => :environment do
backup_path_repo = File.join(Gitlab.config.backup.path, "repositories")
FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
puts "Dumping repositories ...".blue
Project.find_each(:batch_size => 1000) do |project|
print " * #{project.path_with_namespace} ... "
if project.empty_repo?
puts "[SKIPPED]".cyan
next
end
# Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_path_repo, project.namespace.path)) if project.namespace
# Build a destination path for backup
path_to_bundle = File.join(backup_path_repo, project.path_with_namespace + ".bundle")
if Kernel.system("cd #{project.repository.path_to_repo} > /dev/null 2>&1 && git bundle create #{path_to_bundle} --all > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
Backup::Repository.new.dump
puts "done".green
end
task :restore => :environment do
backup_path_repo = File.join(Gitlab.config.backup.path, "repositories")
repos_path = Gitlab.config.gitlab_shell.repos_path
puts "Restoring repositories ... "
Project.find_each(:batch_size => 1000) do |project|
print "#{project.path_with_namespace} ... "
if project.namespace
project.namespace.ensure_dir_exist
end
# Build a backup path
path_to_bundle = File.join(backup_path_repo, project.path_with_namespace + ".bundle")
if Kernel.system("git clone --bare #{path_to_bundle} #{project.repository.path_to_repo} > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
puts "Restoring repositories ...".blue
Backup::Repository.new.restore
puts "done".green
end
end
namespace :db do
task :create => :environment do
puts "Dumping database ... ".blue
Backup.new.backup_db
Backup::Database.new.dump
puts "done".green
end
task :restore => :environment do
puts "Restoring database ... ".blue
Backup.new.restore_db
Backup::Database.new.restore
puts "done".green
end
end