diff --git a/doc/install/databases.md b/doc/install/databases.md index fade5d4f072..a198e726aa0 100644 --- a/doc/install/databases.md +++ b/doc/install/databases.md @@ -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 diff --git a/lib/backup.rb b/lib/backup.rb deleted file mode 100644 index ad80ab1c7f7..00000000000 --- a/lib/backup.rb +++ /dev/null @@ -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 diff --git a/lib/backup/database.rb b/lib/backup/database.rb new file mode 100644 index 00000000000..cfa9971670c --- /dev/null +++ b/lib/backup/database.rb @@ -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 diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb new file mode 100644 index 00000000000..c1d089642b3 --- /dev/null +++ b/lib/backup/repository.rb @@ -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 diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 393f8166877..1f863424382 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -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