Commit 5f7574cf authored by Valery Sizov's avatar Valery Sizov

Merge branch 'backup-cron-mode' into 'master'

Backup cron mode

If you have your server configured to receive emails containing the output of
cron jobs it is annoying to get long spammy emails from the backup script. This
change adds a 'cron mode' that makes the backup script silent unless something
goes wrong during the backup.

See merge request !1268
parents a7ddcab7 458f8c1f
v 7.6.0 v 7.6.0
- Fork repository to groups - Fork repository to groups
- New rugged version - New rugged version
- - Add CRON=1 backup setting for quiet backups
- -
- -
- -
......
...@@ -203,5 +203,8 @@ Add the following lines at the bottom: ...@@ -203,5 +203,8 @@ Add the following lines at the bottom:
``` ```
# Create a full backup of the GitLab repositories and SQL database every day at 4am # Create a full backup of the GitLab repositories and SQL database every day at 4am
0 4 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production 0 4 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production CRON=1
``` ```
The `CRON=1` environment setting tells the backup script to suppress all progress output if there are no errors.
This is recommended to reduce cron spam.
...@@ -13,10 +13,10 @@ module Backup ...@@ -13,10 +13,10 @@ module Backup
def dump def dump
success = case config["adapter"] success = case config["adapter"]
when /^mysql/ then when /^mysql/ then
print "Dumping MySQL database #{config['database']} ... " $progress.print "Dumping MySQL database #{config['database']} ... "
system('mysqldump', *mysql_args, config['database'], out: db_file_name) system('mysqldump', *mysql_args, config['database'], out: db_file_name)
when "postgresql" then when "postgresql" then
print "Dumping PostgreSQL database #{config['database']} ... " $progress.print "Dumping PostgreSQL database #{config['database']} ... "
pg_env pg_env
system('pg_dump', config['database'], out: db_file_name) system('pg_dump', config['database'], out: db_file_name)
end end
...@@ -27,10 +27,10 @@ module Backup ...@@ -27,10 +27,10 @@ module Backup
def restore def restore
success = case config["adapter"] success = case config["adapter"]
when /^mysql/ then when /^mysql/ then
print "Restoring MySQL database #{config['database']} ... " $progress.print "Restoring MySQL database #{config['database']} ... "
system('mysql', *mysql_args, config['database'], in: db_file_name) system('mysql', *mysql_args, config['database'], in: db_file_name)
when "postgresql" then when "postgresql" then
print "Restoring PostgreSQL database #{config['database']} ... " $progress.print "Restoring PostgreSQL database #{config['database']} ... "
# Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE
# statements like MySQL. # statements like MySQL.
Rake::Task["gitlab:db:drop_all_tables"].invoke Rake::Task["gitlab:db:drop_all_tables"].invoke
...@@ -69,9 +69,9 @@ module Backup ...@@ -69,9 +69,9 @@ module Backup
def report_success(success) def report_success(success)
if success if success
puts '[DONE]'.green $progress.puts '[DONE]'.green
else else
puts '[FAILED]'.red $progress.puts '[FAILED]'.red
end end
end end
end end
......
...@@ -18,11 +18,11 @@ module Backup ...@@ -18,11 +18,11 @@ module Backup
end end
# create archive # create archive
print "Creating backup archive: #{tar_file} ... " $progress.print "Creating backup archive: #{tar_file} ... "
if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS) if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS)
puts "done".green $progress.puts "done".green
else else
puts "failed".red puts "creating archive #{tar_file} failed".red
abort 'Backup failed' abort 'Backup failed'
end end
...@@ -31,37 +31,37 @@ module Backup ...@@ -31,37 +31,37 @@ module Backup
def upload(tar_file) def upload(tar_file)
remote_directory = Gitlab.config.backup.upload.remote_directory remote_directory = Gitlab.config.backup.upload.remote_directory
print "Uploading backup archive to remote storage #{remote_directory} ... " $progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
connection_settings = Gitlab.config.backup.upload.connection connection_settings = Gitlab.config.backup.upload.connection
if connection_settings.blank? if connection_settings.blank?
puts "skipped".yellow $progress.puts "skipped".yellow
return return
end end
connection = ::Fog::Storage.new(connection_settings) connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.get(remote_directory) directory = connection.directories.get(remote_directory)
if directory.files.create(key: tar_file, body: File.open(tar_file), public: false) if directory.files.create(key: tar_file, body: File.open(tar_file), public: false)
puts "done".green $progress.puts "done".green
else else
puts "failed".red puts "uploading backup to #{remote_directory} failed".red
abort 'Backup failed' abort 'Backup failed'
end end
end end
def cleanup def cleanup
print "Deleting tmp directories ... " $progress.print "Deleting tmp directories ... "
if Kernel.system('rm', '-rf', *BACKUP_CONTENTS) if Kernel.system('rm', '-rf', *BACKUP_CONTENTS)
puts "done".green $progress.puts "done".green
else else
puts "failed".red puts "deleting tmp directory failed".red
abort 'Backup failed' abort 'Backup failed'
end end
end end
def remove_old def remove_old
# delete backups # delete backups
print "Deleting old backups ... " $progress.print "Deleting old backups ... "
keep_time = Gitlab.config.backup.keep_time.to_i keep_time = Gitlab.config.backup.keep_time.to_i
path = Gitlab.config.backup.path path = Gitlab.config.backup.path
...@@ -76,9 +76,9 @@ module Backup ...@@ -76,9 +76,9 @@ module Backup
end end
end end
end end
puts "done. (#{removed} removed)".green $progress.puts "done. (#{removed} removed)".green
else else
puts "skipping".yellow $progress.puts "skipping".yellow
end end
end end
...@@ -101,12 +101,12 @@ module Backup ...@@ -101,12 +101,12 @@ module Backup
exit 1 exit 1
end end
print "Unpacking backup ... " $progress.print "Unpacking backup ... "
unless Kernel.system(*%W(tar -xf #{tar_file})) unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "failed".red puts "unpacking backup failed".red
exit 1 exit 1
else else
puts "done".green $progress.puts "done".green
end end
settings = YAML.load_file("backup_information.yml") settings = YAML.load_file("backup_information.yml")
......
...@@ -8,19 +8,21 @@ module Backup ...@@ -8,19 +8,21 @@ module Backup
prepare prepare
Project.find_each(batch_size: 1000) do |project| Project.find_each(batch_size: 1000) do |project|
print " * #{project.path_with_namespace} ... " $progress.print " * #{project.path_with_namespace} ... "
# Create namespace dir if missing # Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if project.empty_repo? if project.empty_repo?
puts "[SKIPPED]".cyan $progress.puts "[SKIPPED]".cyan
else else
output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all)) cmd = %W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all)
output, status = Gitlab::Popen.popen(cmd)
if status.zero? if status.zero?
puts "[DONE]".green $progress.puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
puts "failed: #{cmd.join(' ')}"
puts output puts output
abort 'Backup failed' abort 'Backup failed'
end end
...@@ -29,15 +31,17 @@ module Backup ...@@ -29,15 +31,17 @@ module Backup
wiki = ProjectWiki.new(project) wiki = ProjectWiki.new(project)
if File.exists?(path_to_repo(wiki)) if File.exists?(path_to_repo(wiki))
print " * #{wiki.path_with_namespace} ... " $progress.print " * #{wiki.path_with_namespace} ... "
if wiki.repository.empty? if wiki.repository.empty?
puts " [SKIPPED]".cyan $progress.puts " [SKIPPED]".cyan
else else
output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)) cmd = %W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)
output, status = Gitlab::Popen.popen(cmd)
if status.zero? if status.zero?
puts " [DONE]".green $progress.puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
puts "failed: #{cmd.join(' ')}"
abort 'Backup failed' abort 'Backup failed'
end end
end end
...@@ -55,7 +59,7 @@ module Backup ...@@ -55,7 +59,7 @@ module Backup
FileUtils.mkdir_p(repos_path) FileUtils.mkdir_p(repos_path)
Project.find_each(batch_size: 1000) do |project| Project.find_each(batch_size: 1000) do |project|
print "#{project.path_with_namespace} ... " $progress.print "#{project.path_with_namespace} ... "
project.namespace.ensure_dir_exist if project.namespace project.namespace.ensure_dir_exist if project.namespace
...@@ -66,30 +70,35 @@ module Backup ...@@ -66,30 +70,35 @@ module Backup
end end
if system(*cmd, silent) if system(*cmd, silent)
puts "[DONE]".green $progress.puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
puts "failed: #{cmd.join(' ')}"
abort 'Restore failed' abort 'Restore failed'
end end
wiki = ProjectWiki.new(project) wiki = ProjectWiki.new(project)
if File.exists?(path_to_bundle(wiki)) if File.exists?(path_to_bundle(wiki))
print " * #{wiki.path_with_namespace} ... " $progress.print " * #{wiki.path_with_namespace} ... "
if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent) cmd = %W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)})
puts " [DONE]".green if system(*cmd, silent)
$progress.puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
puts "failed: #{cmd.join(' ')}"
abort 'Restore failed' abort 'Restore failed'
end end
end end
end end
print 'Put GitLab hooks in repositories dirs'.yellow $progress.print 'Put GitLab hooks in repositories dirs'.yellow
if system("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks") cmd = "#{Gitlab.config.gitlab_shell.path}/bin/create-hooks"
puts " [DONE]".green if system(cmd)
$progress.puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
puts "failed: #{cmd}"
end end
end end
......
...@@ -6,6 +6,7 @@ namespace :gitlab do ...@@ -6,6 +6,7 @@ namespace :gitlab do
desc "GITLAB | Create a backup of the GitLab system" desc "GITLAB | Create a backup of the GitLab system"
task create: :environment do task create: :environment do
warn_user_is_not_gitlab warn_user_is_not_gitlab
configure_cron_mode
Rake::Task["gitlab:backup:db:create"].invoke Rake::Task["gitlab:backup:db:create"].invoke
Rake::Task["gitlab:backup:repo:create"].invoke Rake::Task["gitlab:backup:repo:create"].invoke
...@@ -21,6 +22,7 @@ namespace :gitlab do ...@@ -21,6 +22,7 @@ namespace :gitlab do
desc "GITLAB | Restore a previously created backup" desc "GITLAB | Restore a previously created backup"
task restore: :environment do task restore: :environment do
warn_user_is_not_gitlab warn_user_is_not_gitlab
configure_cron_mode
backup = Backup::Manager.new backup = Backup::Manager.new
backup.unpack backup.unpack
...@@ -35,43 +37,54 @@ namespace :gitlab do ...@@ -35,43 +37,54 @@ namespace :gitlab do
namespace :repo do namespace :repo do
task create: :environment do task create: :environment do
puts "Dumping repositories ...".blue $progress.puts "Dumping repositories ...".blue
Backup::Repository.new.dump Backup::Repository.new.dump
puts "done".green $progress.puts "done".green
end end
task restore: :environment do task restore: :environment do
puts "Restoring repositories ...".blue $progress.puts "Restoring repositories ...".blue
Backup::Repository.new.restore Backup::Repository.new.restore
puts "done".green $progress.puts "done".green
end end
end end
namespace :db do namespace :db do
task create: :environment do task create: :environment do
puts "Dumping database ... ".blue $progress.puts "Dumping database ... ".blue
Backup::Database.new.dump Backup::Database.new.dump
puts "done".green $progress.puts "done".green
end end
task restore: :environment do task restore: :environment do
puts "Restoring database ... ".blue $progress.puts "Restoring database ... ".blue
Backup::Database.new.restore Backup::Database.new.restore
puts "done".green $progress.puts "done".green
end end
end end
namespace :uploads do namespace :uploads do
task create: :environment do task create: :environment do
puts "Dumping uploads ... ".blue $progress.puts "Dumping uploads ... ".blue
Backup::Uploads.new.dump Backup::Uploads.new.dump
puts "done".green $progress.puts "done".green
end end
task restore: :environment do task restore: :environment do
puts "Restoring uploads ... ".blue $progress.puts "Restoring uploads ... ".blue
Backup::Uploads.new.restore Backup::Uploads.new.restore
puts "done".green $progress.puts "done".green
end
end
def configure_cron_mode
if ENV['CRON']
# We need an object we can say 'puts' and 'print' to; let's use a
# StringIO.
require 'stringio'
$progress = StringIO.new
else
$progress = $stdout
end end
end end
end # namespace end: backup end # namespace end: backup
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment