Commit 75dfd07c authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'skip-tar' into 'master'

Allow user to skip archive creation on backup

See merge request gitlab-org/gitlab!25003
parents e94f6229 12e373af
...@@ -271,6 +271,31 @@ For installations from source: ...@@ -271,6 +271,31 @@ For installations from source:
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production
``` ```
### Skipping tar creation
The last part of creating a backup is generation of a `.tar` file containing
all the parts. In some cases (for example, if the backup is picked up by other
backup software) creating a `.tar` file might be wasted effort or even directly
harmful, so you can skip this step by adding `tar` to the `SKIP` environment
variable.
Adding `tar` to the `SKIP` variable leaves the files and directories containing the
backup in the directory used for the intermediate files. These files will be
overwritten when a new backup is created, so you should make sure they are copied
elsewhere, because you can only have one backup on the system.
For Omnibus GitLab packages:
```shell
sudo gitlab-backup create SKIP=tar
```
For installations from source:
```shell
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=tar RAILS_ENV=production
```
### Uploading backups to a remote (cloud) storage ### Uploading backups to a remote (cloud) storage
Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates. Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates.
...@@ -658,6 +683,10 @@ lose access to your GitLab server. ...@@ -658,6 +683,10 @@ lose access to your GitLab server.
You may also want to restore any TLS keys, certificates, or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). You may also want to restore any TLS keys, certificates, or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
Starting with GitLab 12.9 if an untarred backup (like the ones made with
`SKIP=tar`) is found, and no backup is chosen with `BACKUP=<timestamp>`, the
untarred backup is used.
Depending on your case, you might want to run the restore command with one or Depending on your case, you might want to run the restore command with one or
more of the following options: more of the following options:
......
...@@ -12,7 +12,7 @@ module Backup ...@@ -12,7 +12,7 @@ module Backup
@progress = progress @progress = progress
end end
def pack def write_info
# Make sure there is a connection # Make sure there is a connection
ActiveRecord::Base.connection.reconnect! ActiveRecord::Base.connection.reconnect!
...@@ -20,7 +20,11 @@ module Backup ...@@ -20,7 +20,11 @@ module Backup
File.open("#{backup_path}/backup_information.yml", "w+") do |file| File.open("#{backup_path}/backup_information.yml", "w+") do |file|
file << backup_information.to_yaml.gsub(/^---\n/, '') file << backup_information.to_yaml.gsub(/^---\n/, '')
end end
end
end
def pack
Dir.chdir(backup_path) do
# create archive # create archive
progress.print "Creating backup archive: #{tar_file} ... " progress.print "Creating backup archive: #{tar_file} ... "
# Set file permissions on open to prevent chmod races. # Set file permissions on open to prevent chmod races.
...@@ -31,8 +35,6 @@ module Backup ...@@ -31,8 +35,6 @@ module Backup
puts "creating archive #{tar_file} failed".color(:red) puts "creating archive #{tar_file} failed".color(:red)
raise Backup::Error, 'Backup failed' raise Backup::Error, 'Backup failed'
end end
upload
end end
end end
...@@ -105,8 +107,30 @@ module Backup ...@@ -105,8 +107,30 @@ module Backup
end end
end end
# rubocop: disable Metrics/AbcSize def verify_backup_version
Dir.chdir(backup_path) do
# restoring mismatching backups can lead to unexpected problems
if settings[:gitlab_version] != Gitlab::VERSION
progress.puts(<<~HEREDOC.color(:red))
GitLab version mismatch:
Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!
Please switch to the following version and try again:
version: #{settings[:gitlab_version]}
HEREDOC
progress.puts
progress.puts "Hint: git checkout v#{settings[:gitlab_version]}"
exit 1
end
end
end
def unpack def unpack
if ENV['BACKUP'].blank? && non_tarred_backup?
progress.puts "Non tarred backup found in #{backup_path}, using that"
return false
end
Dir.chdir(backup_path) do Dir.chdir(backup_path) do
# check for existing backups in the backup dir # check for existing backups in the backup dir
if backup_file_list.empty? if backup_file_list.empty?
...@@ -141,21 +165,6 @@ module Backup ...@@ -141,21 +165,6 @@ module Backup
progress.puts 'unpacking backup failed'.color(:red) progress.puts 'unpacking backup failed'.color(:red)
exit 1 exit 1
end end
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
# restoring mismatching backups can lead to unexpected problems
if settings[:gitlab_version] != Gitlab::VERSION
progress.puts(<<~HEREDOC.color(:red))
GitLab version mismatch:
Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!
Please switch to the following version and try again:
version: #{settings[:gitlab_version]}
HEREDOC
progress.puts
progress.puts "Hint: git checkout v#{settings[:gitlab_version]}"
exit 1
end
end end
end end
...@@ -170,6 +179,10 @@ module Backup ...@@ -170,6 +179,10 @@ module Backup
private private
def non_tarred_backup?
File.exist?(File.join(backup_path, 'backup_information.yml'))
end
def backup_path def backup_path
Gitlab.config.backup.path Gitlab.config.backup.path
end end
...@@ -252,7 +265,7 @@ module Backup ...@@ -252,7 +265,7 @@ module Backup
def create_attributes def create_attributes
attrs = { attrs = {
key: remote_target, key: remote_target,
body: File.open(tar_file), body: File.open(File.join(backup_path, tar_file)),
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
encryption: Gitlab.config.backup.upload.encryption, encryption: Gitlab.config.backup.upload.encryption,
encryption_key: Gitlab.config.backup.upload.encryption_key, encryption_key: Gitlab.config.backup.upload.encryption_key,
......
...@@ -17,9 +17,16 @@ namespace :gitlab do ...@@ -17,9 +17,16 @@ namespace :gitlab do
Rake::Task['gitlab:backup:registry:create'].invoke Rake::Task['gitlab:backup:registry:create'].invoke
backup = Backup::Manager.new(progress) backup = Backup::Manager.new(progress)
backup.pack backup.write_info
backup.cleanup
backup.remove_old if ENV['SKIP'] && ENV['SKIP'].include?('tar')
backup.upload
else
backup.pack
backup.upload
backup.cleanup
backup.remove_old
end
progress.puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \ progress.puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
"and are not included in this backup. You will need these files to restore a backup.\n" \ "and are not included in this backup. You will need these files to restore a backup.\n" \
...@@ -33,7 +40,8 @@ namespace :gitlab do ...@@ -33,7 +40,8 @@ namespace :gitlab do
warn_user_is_not_gitlab warn_user_is_not_gitlab
backup = Backup::Manager.new(progress) backup = Backup::Manager.new(progress)
backup.unpack cleanup_required = backup.unpack
backup.verify_backup_version
unless backup.skipped?('db') unless backup.skipped?('db')
begin begin
...@@ -72,7 +80,10 @@ namespace :gitlab do ...@@ -72,7 +80,10 @@ namespace :gitlab do
Rake::Task['gitlab:shell:setup'].invoke Rake::Task['gitlab:shell:setup'].invoke
Rake::Task['cache:clear'].invoke Rake::Task['cache:clear'].invoke
backup.cleanup if cleanup_required
backup.cleanup
end
puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \ puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
"and are not included in this backup. You will need to restore these files manually.".color(:red) "and are not included in this backup. You will need to restore these files manually.".color(:red)
puts "Restore task is done." puts "Restore task is done."
......
...@@ -214,6 +214,30 @@ describe Backup::Manager do ...@@ -214,6 +214,30 @@ describe Backup::Manager do
end end
end end
describe 'verify_backup_version' do
context 'on version mismatch' do
let(:gitlab_version) { Gitlab::VERSION }
it 'stops the process' do
allow(YAML).to receive(:load_file)
.and_return({ gitlab_version: "not #{gitlab_version}" })
expect { subject.verify_backup_version }.to raise_error SystemExit
end
end
context 'on version match' do
let(:gitlab_version) { Gitlab::VERSION }
it 'does nothing' do
allow(YAML).to receive(:load_file)
.and_return({ gitlab_version: "#{gitlab_version}" })
expect { subject.verify_backup_version }.not_to raise_error
end
end
end
describe '#unpack' do describe '#unpack' do
context 'when there are no backup files in the directory' do context 'when there are no backup files in the directory' do
before do before do
...@@ -292,6 +316,23 @@ describe Backup::Manager do ...@@ -292,6 +316,23 @@ describe Backup::Manager do
expect(progress).to have_received(:puts).with(a_string_matching('done')) expect(progress).to have_received(:puts).with(a_string_matching('done'))
end end
end end
context 'when there is a non-tarred backup in the directory' do
before do
allow(Dir).to receieve(:glob).and_return(
[
'backup_information.yml'
]
)
it 'selects the non-tarred backup to restore from' do
expect { subject.unpack }.to output.to_stdout
expect(progress).to have_received(:puts)
.with(a_string_matching('Non tarred backup found '))
expect(Kernel).not_to receive(:system)
end
end
end
end end
describe '#upload' do describe '#upload' do
...@@ -329,9 +370,7 @@ describe Backup::Manager do ...@@ -329,9 +370,7 @@ describe Backup::Manager do
.with(hash_including(key: backup_filename, public: false)) .with(hash_including(key: backup_filename, public: false))
.and_return(true) .and_return(true)
Dir.chdir(Gitlab.config.backup.path) do subject.upload
subject.upload
end
end end
it 'adds the DIRECTORY environment variable if present' do it 'adds the DIRECTORY environment variable if present' do
...@@ -341,9 +380,7 @@ describe Backup::Manager do ...@@ -341,9 +380,7 @@ describe Backup::Manager do
.with(hash_including(key: "daily/#{backup_filename}", public: false)) .with(hash_including(key: "daily/#{backup_filename}", public: false))
.and_return(true) .and_return(true)
Dir.chdir(Gitlab.config.backup.path) do subject.upload
subject.upload
end
end end
end end
...@@ -373,9 +410,7 @@ describe Backup::Manager do ...@@ -373,9 +410,7 @@ describe Backup::Manager do
.with(hash_excluding(public: false)) .with(hash_excluding(public: false))
.and_return(true) .and_return(true)
Dir.chdir(Gitlab.config.backup.path) do subject.upload
subject.upload
end
end end
end end
end end
......
...@@ -14,6 +14,14 @@ describe 'gitlab:app namespace rake task' do ...@@ -14,6 +14,14 @@ describe 'gitlab:app namespace rake task' do
tars_glob.first tars_glob.first
end end
def backup_files
%w(backup_information.yml artifacts.tar.gz builds.tar.gz lfs.tar.gz pages.tar.gz)
end
def backup_directories
%w(db repositories)
end
before(:all) do before(:all) do
Rake.application.rake_require 'tasks/gitlab/helpers' Rake.application.rake_require 'tasks/gitlab/helpers'
Rake.application.rake_require 'tasks/gitlab/backup' Rake.application.rake_require 'tasks/gitlab/backup'
...@@ -28,12 +36,16 @@ describe 'gitlab:app namespace rake task' do ...@@ -28,12 +36,16 @@ describe 'gitlab:app namespace rake task' do
before do before do
stub_env('force', 'yes') stub_env('force', 'yes')
FileUtils.rm(tars_glob, force: true) FileUtils.rm(tars_glob, force: true)
FileUtils.rm(backup_files, force: true)
FileUtils.rm_rf(backup_directories, secure: true)
reenable_backup_sub_tasks reenable_backup_sub_tasks
stub_container_registry_config(enabled: enable_registry) stub_container_registry_config(enabled: enable_registry)
end end
after do after do
FileUtils.rm(tars_glob, force: true) FileUtils.rm(tars_glob, force: true)
FileUtils.rm(backup_files, force: true)
FileUtils.rm_rf(backup_directories, secure: true)
end end
def run_rake_task(task_name) def run_rake_task(task_name)
...@@ -62,15 +74,6 @@ describe 'gitlab:app namespace rake task' do ...@@ -62,15 +74,6 @@ describe 'gitlab:app namespace rake task' do
let(:gitlab_version) { Gitlab::VERSION } let(:gitlab_version) { Gitlab::VERSION }
it 'fails on mismatch' do
allow(YAML).to receive(:load_file)
.and_return({ gitlab_version: "not #{gitlab_version}" })
expect do
expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout
end.to raise_error(SystemExit)
end
context 'restore with matching gitlab version' do context 'restore with matching gitlab version' do
before do before do
allow(YAML).to receive(:load_file) allow(YAML).to receive(:load_file)
...@@ -241,7 +244,7 @@ describe 'gitlab:app namespace rake task' do ...@@ -241,7 +244,7 @@ describe 'gitlab:app namespace rake task' do
) )
expect(exit_status).to eq(0) expect(exit_status).to eq(0)
expect(tar_contents).to match('db/') expect(tar_contents).to match('db')
expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('uploads.tar.gz')
expect(tar_contents).to match('repositories/') expect(tar_contents).to match('repositories/')
expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('builds.tar.gz')
...@@ -379,6 +382,50 @@ describe 'gitlab:app namespace rake task' do ...@@ -379,6 +382,50 @@ describe 'gitlab:app namespace rake task' do
end end
end end
describe 'skipping tar archive creation' do
before do
stub_env('SKIP', 'tar')
end
it 'created files with backup content and no tar archive' do
expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout
dir_contents = Dir.children(Gitlab.config.backup.path)
expect(dir_contents).to contain_exactly(
'backup_information.yml',
'db',
'uploads.tar.gz',
'builds.tar.gz',
'artifacts.tar.gz',
'lfs.tar.gz',
'pages.tar.gz',
'registry.tar.gz',
'repositories',
'tmp'
)
end
it 'those component files can be restored from' do
expect { run_rake_task("gitlab:backup:create") }.to output.to_stdout
allow(Rake::Task['gitlab:shell:setup'])
.to receive(:invoke).and_return(true)
expect(Rake::Task['gitlab:db:drop_tables']).to receive :invoke
expect(Rake::Task['gitlab:backup:db:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:repo:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:uploads:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:builds:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:pages:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:registry:restore']).to receive :invoke
expect(Rake::Task['gitlab:shell:setup']).to receive :invoke
expect { run_rake_task("gitlab:backup:restore") }.to output.to_stdout
end
end
describe "Human Readable Backup Name" do describe "Human Readable Backup Name" do
it 'name has human readable time' do it 'name has human readable time' do
expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout
......
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