Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/backup')
-rw-r--r--spec/lib/backup/files_spec.rb390
-rw-r--r--spec/lib/backup/gitaly_backup_spec.rb12
-rw-r--r--spec/lib/backup/manager_spec.rb188
-rw-r--r--spec/lib/backup/options_spec.rb16
-rw-r--r--spec/lib/backup/targets/database_spec.rb (renamed from spec/lib/backup/database_spec.rb)46
-rw-r--r--spec/lib/backup/targets/files_spec.rb403
-rw-r--r--spec/lib/backup/targets/repositories_spec.rb (renamed from spec/lib/backup/repositories_spec.rb)126
-rw-r--r--spec/lib/backup/targets/target_spec.rb36
-rw-r--r--spec/lib/backup/task_spec.rb22
9 files changed, 657 insertions, 582 deletions
diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb
deleted file mode 100644
index 3c96628b4cf..00000000000
--- a/spec/lib/backup/files_spec.rb
+++ /dev/null
@@ -1,390 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Backup::Files, feature_category: :backup_restore do
- let(:progress) { StringIO.new }
- let!(:project) { create(:project) }
- let(:backup_options) { Backup::Options.new }
-
- let(:status_0) { double('exit 0', success?: true, exitstatus: 0) }
- let(:status_1) { double('exit 1', success?: false, exitstatus: 1) }
- let(:status_2) { double('exit 2', success?: false, exitstatus: 2) }
-
- before do
- allow(progress).to receive(:puts)
- allow(progress).to receive(:print)
- allow(FileUtils).to receive(:mkdir_p).and_return(true)
- allow(FileUtils).to receive(:mv).and_return(true)
- allow(File).to receive(:exist?).and_return(true)
- allow(File).to receive(:realpath).with("/var/gitlab-registry").and_return("/var/gitlab-registry")
- allow(File).to receive(:realpath).with("/var/gitlab-registry/..").and_return("/var")
- allow(File).to receive(:realpath).with("/var/gitlab-pages").and_return("/var/gitlab-pages")
- allow(File).to receive(:realpath).with("/var/gitlab-pages/..").and_return("/var")
-
- allow_any_instance_of(described_class).to receive(:progress).and_return(progress)
- end
-
- RSpec::Matchers.define :eq_statuslist do |expected|
- match do |actual|
- actual.map(&:exitstatus) == expected.map(&:exitstatus)
- end
-
- description do
- 'be an Array of Process::Status with equal exitstatus against expected'
- end
-
- failure_message do |actual|
- "expected #{actual} exitstatuses list to be equal #{expected} exitstatuses list"
- end
- end
-
- describe '#restore' do
- subject { described_class.new(progress, '/var/gitlab-registry', options: backup_options) }
-
- let(:timestamp) { Time.utc(2017, 3, 22) }
-
- around do |example|
- travel_to(timestamp) { example.run }
- end
-
- describe 'folders with permission' do
- before do
- allow(subject).to receive(:run_pipeline!).and_return([[true, true], ''])
- allow(subject).to receive(:backup_existing_files).and_return(true)
- allow(subject).to receive(:pipeline_succeeded?).and_return(true)
- allow(Dir).to receive(:glob).with("/var/gitlab-registry/*", File::FNM_DOTMATCH).and_return(["/var/gitlab-registry/.", "/var/gitlab-registry/..", "/var/gitlab-registry/sample1"])
- end
-
- it 'moves all necessary files' do
- allow(subject).to receive(:backup_existing_files).and_call_original
- expect(FileUtils).to receive(:mv).with(["/var/gitlab-registry/sample1"], File.join(Gitlab.config.backup.path, "tmp", "registry.#{Time.now.to_i}"))
- subject.restore('registry.tar.gz', 'backup_id')
- end
-
- it 'raises no errors' do
- expect { subject.restore('registry.tar.gz', 'backup_id') }.not_to raise_error
- end
-
- it 'calls tar command with unlink' do
- expect(subject).to receive(:tar).and_return('blabla-tar')
-
- expect(subject).to receive(:run_pipeline!).with(["gzip -cd", %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]], any_args)
- expect(subject).to receive(:pipeline_succeeded?).and_return(true)
- subject.restore('registry.tar.gz', 'backup_id')
- end
-
- it 'raises an error on failure' do
- expect(subject).to receive(:pipeline_succeeded?).and_return(false)
-
- expect { subject.restore('registry.tar.gz', 'backup_id') }.to raise_error(/Restore operation failed:/)
- end
- end
-
- describe 'folders without permissions' do
- before do
- allow(FileUtils).to receive(:mv).and_raise(Errno::EACCES)
- allow(subject).to receive(:run_pipeline!).and_return([[true, true], ''])
- allow(subject).to receive(:pipeline_succeeded?).and_return(true)
- end
-
- it 'shows error message' do
- expect(subject).to receive(:access_denied_error).with("/var/gitlab-registry")
- subject.restore('registry.tar.gz', 'backup_id')
- end
- end
-
- describe 'folders that are a mountpoint' do
- before do
- allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY)
- allow(subject).to receive(:run_pipeline!).and_return([[true, true], ''])
- allow(subject).to receive(:pipeline_succeeded?).and_return(true)
- end
-
- it 'shows error message' do
- expect(subject).to receive(:resource_busy_error).with("/var/gitlab-registry")
- .and_call_original
-
- expect { subject.restore('registry.tar.gz', 'backup_id') }.to raise_error(/is a mountpoint/)
- end
- end
-
- describe 'with DECOMPRESS_CMD' do
- before do
- stub_env('DECOMPRESS_CMD', 'tee')
- allow(subject).to receive(:pipeline_succeeded?).and_return(true)
- end
-
- it 'passes through tee instead of gzip' do
- expect(subject).to receive(:run_pipeline!).with(['tee', anything], any_args).and_return([[true, true], ''])
-
- expect do
- subject.restore('registry.tar.gz', 'backup_id')
- end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
- end
- end
- end
-
- describe '#dump' do
- subject do
- described_class.new(progress, '/var/gitlab-pages', excludes: ['@pages.tmp'], options: backup_options)
- end
-
- before do
- allow(subject).to receive(:run_pipeline!).and_return([[true, true], ''])
- allow(subject).to receive(:pipeline_succeeded?).and_return(true)
- end
-
- it 'raises no errors' do
- expect { subject.dump('registry.tar.gz', 'backup_id') }.not_to raise_error
- end
-
- it 'excludes tmp dirs from archive' do
- expect(subject).to receive(:tar).and_return('blabla-tar')
-
- expect(subject).to receive(:run_pipeline!).with([%w[blabla-tar --exclude=lost+found --exclude=./@pages.tmp -C /var/gitlab-pages -cf - .], 'gzip -c -1'], any_args)
- subject.dump('registry.tar.gz', 'backup_id')
- end
-
- it 'raises an error on failure' do
- allow(subject).to receive(:run_pipeline!).and_return([[true, true], ''])
- expect(subject).to receive(:pipeline_succeeded?).and_return(false)
-
- expect do
- subject.dump('registry.tar.gz', 'backup_id')
- end.to raise_error(/Failed to create compressed file/)
- end
-
- describe 'with STRATEGY=copy' do
- before do
- stub_env('STRATEGY', 'copy')
- allow(Gitlab.config.backup).to receive(:path) { '/var/gitlab-backup' }
- allow(File).to receive(:realpath).with("/var/gitlab-backup").and_return("/var/gitlab-backup")
- end
-
- it 'excludes tmp dirs from rsync' do
- expect(Gitlab::Popen).to receive(:popen)
- .with(%w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp /var/gitlab-pages /var/gitlab-backup])
- .and_return(['', 0])
-
- subject.dump('registry.tar.gz', 'backup_id')
- end
-
- it 'retries if rsync fails due to vanishing files' do
- expect(Gitlab::Popen).to receive(:popen)
- .with(%w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp /var/gitlab-pages /var/gitlab-backup])
- .and_return(['rsync failed', 24], ['', 0])
-
- expect do
- subject.dump('registry.tar.gz', 'backup_id')
- end.to output(/files vanished during rsync, retrying/).to_stdout
- end
-
- it 'raises an error and outputs an error message if rsync failed' do
- allow(Gitlab::Popen).to receive(:popen)
- .with(%w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp /var/gitlab-pages /var/gitlab-backup])
- .and_return(['rsync failed', 1])
-
- expect do
- subject.dump('registry.tar.gz', 'backup_id')
- end.to output(/rsync failed/).to_stdout
- .and raise_error(/Failed to create compressed file/)
- end
- end
-
- describe 'with COMPRESS_CMD' do
- before do
- stub_env('COMPRESS_CMD', 'tee')
- end
-
- it 'passes through tee instead of gzip' do
- expect(subject).to receive(:run_pipeline!).with([anything, 'tee'], any_args)
- expect do
- subject.dump('registry.tar.gz', 'backup_id')
- end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
- end
- end
-
- context 'when GZIP_RSYNCABLE is "yes"' do
- before do
- stub_env('GZIP_RSYNCABLE', 'yes')
- end
-
- it 'gzips the files with rsyncable option' do
- expect(subject).to receive(:run_pipeline!).with([anything, 'gzip --rsyncable -c -1'], any_args)
- subject.dump('registry.tar.gz', 'backup_id')
- end
- end
-
- context 'when GZIP_RSYNCABLE is not set' do
- it 'gzips the files without the rsyncable option' do
- expect(subject).to receive(:run_pipeline!).with([anything, 'gzip -c -1'], any_args)
- subject.dump('registry.tar.gz', 'backup_id')
- end
- end
- end
-
- describe '#exclude_dirs' do
- subject do
- described_class.new(progress, '/var/gitlab-pages', excludes: ['@pages.tmp'], options: backup_options)
- end
-
- it 'prepends a leading dot slash to tar excludes' do
- expect(subject.exclude_dirs(:tar)).to eq(['--exclude=lost+found', '--exclude=./@pages.tmp'])
- end
-
- it 'prepends a leading slash and app_files_dir basename to rsync excludes' do
- expect(subject.exclude_dirs(:rsync)).to eq(['--exclude=lost+found', '--exclude=/gitlab-pages/@pages.tmp'])
- end
- end
-
- describe '#run_pipeline!' do
- subject do
- described_class.new(progress, '/var/gitlab-registry', options: backup_options)
- end
-
- it 'executes an Open3.pipeline for cmd_list' do
- expect(Open3).to receive(:pipeline).with(%w[whew command], %w[another cmd], any_args)
-
- subject.run_pipeline!([%w[whew command], %w[another cmd]])
- end
-
- it 'returns an empty output on success pipeline' do
- expect(subject.run_pipeline!(%w[true true])[1]).to eq('')
- end
-
- it 'returns the stderr for failed pipeline' do
- expect(
- subject.run_pipeline!(['echo OMG: failed command present 1>&2; false', 'true'])[1]
- ).to match(/OMG: failed/)
- end
-
- it 'returns the success status list on success pipeline' do
- expect(
- subject.run_pipeline!(%w[true true])[0]
- ).to eq_statuslist([status_0, status_0])
- end
-
- it 'returns the failed status in status list for failed commands in pipeline' do
- expect(subject.run_pipeline!(%w[false true true])[0]).to eq_statuslist([status_1, status_0, status_0])
- expect(subject.run_pipeline!(%w[true false true])[0]).to eq_statuslist([status_0, status_1, status_0])
- expect(subject.run_pipeline!(%w[false false true])[0]).to eq_statuslist([status_1, status_1, status_0])
- expect(subject.run_pipeline!(%w[false true false])[0]).to eq_statuslist([status_1, status_0, status_1])
- expect(subject.run_pipeline!(%w[false false false])[0]).to eq_statuslist([status_1, status_1, status_1])
- end
- end
-
- describe '#pipeline_succeeded?' do
- subject do
- described_class.new(progress, '/var/gitlab-registry', options: backup_options)
- end
-
- it 'returns true if both tar and gzip succeeeded' do
- expect(
- subject.pipeline_succeeded?(tar_status: status_0, compress_status: status_0, output: 'any_output')
- ).to be_truthy
- end
-
- it 'returns false if gzip failed' do
- expect(
- subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_1, output: 'any_output')
- ).to be_falsey
- end
-
- context 'if gzip succeeded and tar failed non-critically' do
- before do
- allow(subject).to receive(:tar_ignore_non_success?).and_return(true)
- end
-
- it 'returns true' do
- expect(
- subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
- ).to be_truthy
- end
- end
-
- context 'if gzip succeeded and tar failed in other cases' do
- before do
- allow(subject).to receive(:tar_ignore_non_success?).and_return(false)
- end
-
- it 'returns false' do
- expect(
- subject.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
- ).to be_falsey
- end
- end
- end
-
- describe '#tar_ignore_non_success?' do
- subject do
- described_class.new(progress, '/var/gitlab-registry', options: backup_options)
- end
-
- context 'if `tar` command exits with 1 exitstatus' do
- it 'returns true' do
- expect(
- subject.tar_ignore_non_success?(1, 'any_output')
- ).to be_truthy
- end
-
- it 'outputs a warning' do
- expect do
- subject.tar_ignore_non_success?(1, 'any_output')
- end.to output(/Ignoring tar exit status 1/).to_stdout
- end
- end
-
- context 'if `tar` command exits with 2 exitstatus with non-critical warning' do
- before do
- allow(subject).to receive(:noncritical_warning?).and_return(true)
- end
-
- it 'returns true' do
- expect(
- subject.tar_ignore_non_success?(2, 'any_output')
- ).to be_truthy
- end
-
- it 'outputs a warning' do
- expect do
- subject.tar_ignore_non_success?(2, 'any_output')
- end.to output(/Ignoring non-success exit status/).to_stdout
- end
- end
-
- context 'if `tar` command exits with any other unlisted error' do
- before do
- allow(subject).to receive(:noncritical_warning?).and_return(false)
- end
-
- it 'returns false' do
- expect(
- subject.tar_ignore_non_success?(2, 'any_output')
- ).to be_falsey
- end
- end
- end
-
- describe '#noncritical_warning?' do
- subject do
- described_class.new(progress, '/var/gitlab-registry', options: backup_options)
- end
-
- it 'returns true if given text matches noncritical warnings list' do
- expect(
- subject.noncritical_warning?('tar: .: Cannot mkdir: No such file or directory')
- ).to be_truthy
-
- expect(
- subject.noncritical_warning?('gtar: .: Cannot mkdir: No such file or directory')
- ).to be_truthy
- end
-
- it 'returns false otherwize' do
- expect(
- subject.noncritical_warning?('unknown message')
- ).to be_falsey
- end
- end
-end
diff --git a/spec/lib/backup/gitaly_backup_spec.rb b/spec/lib/backup/gitaly_backup_spec.rb
index 058c7f12f63..f063462929d 100644
--- a/spec/lib/backup/gitaly_backup_spec.rb
+++ b/spec/lib/backup/gitaly_backup_spec.rb
@@ -10,9 +10,7 @@ RSpec.describe Backup::GitalyBackup, feature_category: :backup_restore do
let(:server_side) { false }
let(:progress) do
- Tempfile.new('progress').tap do |progress|
- progress.unlink
- end
+ Tempfile.new('progress').tap(&:unlink)
end
let(:expected_env) do
@@ -62,8 +60,8 @@ RSpec.describe Backup::GitalyBackup, feature_category: :backup_restore do
subject.finish!
expect(File).to exist(File.join(destination, project.disk_path, backup_id, '001.bundle'))
- expect(File).to exist(File.join(destination, project.disk_path + '.wiki', backup_id, '001.bundle'))
- expect(File).to exist(File.join(destination, project.disk_path + '.design', backup_id, '001.bundle'))
+ expect(File).to exist(File.join(destination, "#{project.disk_path}.wiki", backup_id, '001.bundle'))
+ expect(File).to exist(File.join(destination, "#{project.disk_path}.design", backup_id, '001.bundle'))
expect(File).to exist(File.join(destination, personal_snippet.disk_path, backup_id, '001.bundle'))
expect(File).to exist(File.join(destination, project_snippet.disk_path, backup_id, '001.bundle'))
end
@@ -189,7 +187,7 @@ RSpec.describe Backup::GitalyBackup, feature_category: :backup_restore do
custom_hooks_path = '#{repo.relative_path}.custom_hooks.tar'
TOML
- File.write(File.join(repo_backup_root, 'manifests', repo.storage, repo.relative_path, backup_id + '.toml'), manifest)
+ File.write(File.join(repo_backup_root, 'manifests', repo.storage, repo.relative_path, "#{backup_id}.toml"), manifest)
end
it 'restores from repository bundles', :aggregate_failures do
@@ -209,7 +207,7 @@ RSpec.describe Backup::GitalyBackup, feature_category: :backup_restore do
subject.enqueue(project_snippet, Gitlab::GlRepository::SNIPPET)
subject.finish!
- collect_commit_shas = -> (repo) { repo.commits('master', limit: 10).map(&:sha) }
+ collect_commit_shas = ->(repo) { repo.commits('master', limit: 10).map(&:sha) }
expect(collect_commit_shas.call(project.repository)).to match_array(['393a7d860a5a4c3cc736d7eb00604e3472bb95ec'])
expect(collect_commit_shas.call(project.wiki.repository)).to match_array(['c74b9948d0088d703ee1fafeddd9ed9add2901ea'])
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 7a8cffe8529..99a484c7a6f 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:progress) { StringIO.new }
let(:definitions) { nil }
+ let(:options) { build(:backup_options, :skip_none) }
subject { described_class.new(progress, definitions: definitions) }
@@ -22,32 +23,29 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
describe '#run_create_task' do
- let(:enabled) { true }
- let(:task) { instance_double(Backup::Task) }
+ let(:terraform_state) do
+ Backup::Tasks::TerraformState.new(progress: progress, options: options)
+ .tap { |state| allow(state).to receive(:target).and_return(target) }
+ end
+
+ let(:target) { instance_double(Backup::Targets::Target) }
let(:definitions) do
- {
- 'terraform_state' => Backup::Manager::TaskDefinition.new(
- task: task,
- enabled: enabled,
- destination_path: 'terraform_state.tar.gz',
- human_name: 'terraform state'
- )
- }
+ { 'terraform_state' => terraform_state }
end
it 'calls the named task' do
- expect(task).to receive(:dump)
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform state ... ')
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform state ... done')
+ expect(target).to receive(:dump)
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform states ... ')
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform states ... done')
subject.run_create_task('terraform_state')
end
describe 'disabled' do
- let(:enabled) { false }
-
it 'informs the user' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform state ... [DISABLED]')
+ allow(terraform_state).to receive(:enabled).and_return(false)
+
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform states ... [DISABLED]')
subject.run_create_task('terraform_state')
end
@@ -57,7 +55,7 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
it 'informs the user' do
stub_env('SKIP', 'terraform_state')
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform state ... [SKIPPED]')
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping terraform states ... [SKIPPED]')
subject.run_create_task('terraform_state')
end
@@ -65,17 +63,22 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
describe '#run_restore_task' do
- let(:enabled) { true }
- let(:pre_restore_warning) { nil }
- let(:post_restore_warning) { nil }
- let(:definitions) { { 'terraform_state' => Backup::Manager::TaskDefinition.new(task: task, enabled: enabled, human_name: 'terraform state', destination_path: 'terraform_state.tar.gz') } }
- let(:backup_information) { { backup_created_at: Time.zone.parse('2019-01-01'), gitlab_version: '12.3' } }
- let(:task) do
- instance_double(Backup::Task,
- pre_restore_warning: pre_restore_warning,
- post_restore_warning: post_restore_warning)
+ let(:terraform_state) do
+ Backup::Tasks::TerraformState.new(progress: progress, options: options)
+ .tap { |task| allow(task).to receive(:target).and_return(target) }
end
+ let(:pre_restore_warning) { '' }
+ let(:post_restore_warning) { '' }
+ let(:target) do
+ instance_double(::Backup::Targets::Target,
+ pre_restore_warning: pre_restore_warning,
+ post_restore_warning: post_restore_warning)
+ end
+
+ let(:definitions) { { 'terraform_state' => terraform_state } }
+ let(:backup_information) { { backup_created_at: Time.zone.parse('2019-01-01'), gitlab_version: '12.3' } }
+
before do
allow_next_instance_of(Backup::Metadata) do |metadata|
allow(metadata).to receive(:load_from_file).and_return(backup_information)
@@ -83,18 +86,17 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'calls the named task' do
- expect(task).to receive(:restore)
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... ').ordered
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... done').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... ').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... done').ordered
+ expect(target).to receive(:restore)
subject.run_restore_task('terraform_state')
end
describe 'disabled' do
- let(:enabled) { false }
-
it 'informs the user' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... [DISABLED]').ordered
+ allow(terraform_state).to receive(:enabled).and_return(false)
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... [DISABLED]').ordered
subject.run_restore_task('terraform_state')
end
@@ -104,17 +106,17 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:pre_restore_warning) { 'Watch out!' }
it 'displays and waits for the user' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... ').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... ').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Watch out!').ordered
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... done').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... done').ordered
expect(Gitlab::TaskHelpers).to receive(:ask_to_continue)
- expect(task).to receive(:restore)
+ expect(target).to receive(:restore)
subject.run_restore_task('terraform_state')
end
it 'does not continue when the user quits' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... ').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... ').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Watch out!').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Quitting...').ordered
expect(Gitlab::TaskHelpers).to receive(:ask_to_continue).and_raise(Gitlab::TaskAbortedByUserError)
@@ -129,21 +131,21 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:post_restore_warning) { 'Watch out!' }
it 'displays and waits for the user' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... ').ordered
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... done').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... ').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... done').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Watch out!').ordered
expect(Gitlab::TaskHelpers).to receive(:ask_to_continue)
- expect(task).to receive(:restore)
+ expect(target).to receive(:restore)
subject.run_restore_task('terraform_state')
end
it 'does not continue when the user quits' do
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... ').ordered
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform state ... done').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... ').ordered
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Restoring terraform states ... done').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Watch out!').ordered
expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Quitting...').ordered
- expect(task).to receive(:restore)
+ expect(target).to receive(:restore)
expect(Gitlab::TaskHelpers).to receive(:ask_to_continue).and_raise(Gitlab::TaskAbortedByUserError)
expect do
@@ -163,13 +165,20 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
let(:pack_tar_system_options) { { out: [pack_tar_file, 'w', Gitlab.config.backup.archive_permissions] } }
let(:pack_tar_cmdline) { ['tar', '-cf', '-', *expected_backup_contents, pack_tar_system_options] }
- let(:task1) { instance_double(Backup::Task) }
- let(:task2) { instance_double(Backup::Task) }
+ let(:lfs) do
+ Backup::Tasks::Lfs.new(progress: progress, options: options)
+ .tap { |task| allow(task).to receive(:target).and_return(target1) }
+ end
+
+ let(:pages) do
+ Backup::Tasks::Pages.new(progress: progress, options: options)
+ .tap { |task| allow(task).to receive(:target).and_return(target2) }
+ end
+
+ let(:target1) { instance_double(Backup::Targets::Target) }
+ let(:target2) { instance_double(Backup::Targets::Target) }
let(:definitions) do
- {
- 'lfs' => Backup::Manager::TaskDefinition.new(task: task1, human_name: 'lfs objects', destination_path: 'lfs.tar.gz'),
- 'pages' => Backup::Manager::TaskDefinition.new(task: task2, human_name: 'pages', destination_path: 'pages.tar.gz')
- }
+ { 'lfs' => lfs, 'pages' => pages }
end
before do
@@ -178,8 +187,8 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
allow(Gitlab::BackupLogger).to receive(:info)
allow(Kernel).to receive(:system).and_return(true)
- allow(task1).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
- allow(task2).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
+ allow(target1).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
+ allow(target2).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
end
it 'creates a backup tar' do
@@ -237,11 +246,11 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'when the destination is optional' do
let(:expected_backup_contents) { %w[backup_information.yml lfs.tar.gz] }
- let(:definitions) do
- {
- 'lfs' => Backup::Manager::TaskDefinition.new(task: task1, destination_path: 'lfs.tar.gz'),
- 'pages' => Backup::Manager::TaskDefinition.new(task: task2, destination_path: 'pages.tar.gz', destination_optional: true)
- }
+ let(:pages) do
+ Backup::Tasks::Pages.new(progress: progress, options: options)
+ .tap do |task|
+ allow(task).to receive_messages(target: target2, destination_optional: true)
+ end
end
it 'executes tar' do
@@ -255,17 +264,17 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'many backup files' do
let(:files) do
- [
- '1451606400_2016_01_01_1.2.3_gitlab_backup.tar',
- '1451520000_2015_12_31_4.5.6_gitlab_backup.tar',
- '1451520000_2015_12_31_4.5.6-pre_gitlab_backup.tar',
- '1451520000_2015_12_31_4.5.6-rc1_gitlab_backup.tar',
- '1451520000_2015_12_31_4.5.6-pre-ee_gitlab_backup.tar',
- '1451510000_2015_12_30_gitlab_backup.tar',
- '1450742400_2015_12_22_gitlab_backup.tar',
- '1449878400_gitlab_backup.tar',
- '1449014400_gitlab_backup.tar',
- 'manual_gitlab_backup.tar'
+ %w[
+ 1451606400_2016_01_01_1.2.3_gitlab_backup.tar
+ 1451520000_2015_12_31_4.5.6_gitlab_backup.tar
+ 1451520000_2015_12_31_4.5.6-pre_gitlab_backup.tar
+ 1451520000_2015_12_31_4.5.6-rc1_gitlab_backup.tar
+ 1451520000_2015_12_31_4.5.6-pre-ee_gitlab_backup.tar
+ 1451510000_2015_12_30_gitlab_backup.tar
+ 1450742400_2015_12_22_gitlab_backup.tar
+ 1449878400_gitlab_backup.tar
+ 1449014400_gitlab_backup.tar
+ manual_gitlab_backup.tar
]
end
@@ -295,10 +304,10 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'when no valid file is found' do
let(:files) do
- [
- '14516064000_2016_01_01_1.2.3_gitlab_backup.tar',
- 'foo_1451520000_2015_12_31_4.5.6_gitlab_backup.tar',
- '1451520000_2015_12_31_4.5.6-foo_gitlab_backup.tar'
+ %w[
+ 14516064000_2016_01_01_1.2.3_gitlab_backup.tar
+ foo_1451520000_2015_12_31_4.5.6_gitlab_backup.tar
+ 1451520000_2015_12_31_4.5.6-foo_gitlab_backup.tar
]
end
@@ -654,9 +663,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'when there are two backup files in the directory and BACKUP variable is not set' do
before do
allow(Dir).to receive(:glob).and_return(
- [
- '1451606400_2016_01_01_1.2.3_gitlab_backup.tar',
- '1451520000_2015_12_31_gitlab_backup.tar'
+ %w[
+ 1451606400_2016_01_01_1.2.3_gitlab_backup.tar
+ 1451520000_2015_12_31_gitlab_backup.tar
]
)
end
@@ -923,13 +932,20 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
describe '#restore' do
- let(:task1) { instance_double(Backup::Task, pre_restore_warning: nil, post_restore_warning: nil) }
- let(:task2) { instance_double(Backup::Task, pre_restore_warning: nil, post_restore_warning: nil) }
+ let(:lfs) do
+ Backup::Tasks::Lfs.new(progress: progress, options: options)
+ .tap { |task| allow(task).to receive(:target).and_return(target1) }
+ end
+
+ let(:pages) do
+ Backup::Tasks::Pages.new(progress: progress, options: options)
+ .tap { |task| allow(task).to receive(:target).and_return(target2) }
+ end
+
+ let(:target1) { instance_double(Backup::Targets::Target, pre_restore_warning: nil, post_restore_warning: nil) }
+ let(:target2) { instance_double(Backup::Targets::Target, pre_restore_warning: nil, post_restore_warning: nil) }
let(:definitions) do
- {
- 'lfs' => Backup::Manager::TaskDefinition.new(task: task1, human_name: 'lfs content', destination_path: 'lfs.tar.gz'),
- 'pages' => Backup::Manager::TaskDefinition.new(task: task2, human_name: 'pages', destination_path: 'pages.tar.gz')
- }
+ { 'lfs' => lfs, 'pages' => pages }
end
let(:gitlab_version) { Gitlab::VERSION }
@@ -947,8 +963,8 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
Rake.application.rake_require 'tasks/cache'
allow(Gitlab::BackupLogger).to receive(:info)
- allow(task1).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
- allow(task2).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
+ allow(target1).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
+ allow(target2).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
allow_next_instance_of(Backup::Metadata) do |metadata|
allow(metadata).to receive(:load_from_file).and_return(backup_information)
end
@@ -971,9 +987,9 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
context 'when there are two backup files in the directory and BACKUP variable is not set' do
before do
allow(Dir).to receive(:glob).and_return(
- [
- '1451606400_2016_01_01_1.2.3_gitlab_backup.tar',
- '1451520000_2015_12_31_gitlab_backup.tar'
+ %w[
+ 1451606400_2016_01_01_1.2.3_gitlab_backup.tar
+ 1451520000_2015_12_31_gitlab_backup.tar
]
)
end
@@ -1047,8 +1063,8 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
end
it 'unpacks the BACKUP specified file but uses the backup information backup ID' do
- expect(task1).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
- expect(task2).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
+ expect(target1).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'lfs.tar.gz'), backup_id)
+ expect(target2).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'pages.tar.gz'), backup_id)
subject.restore
diff --git a/spec/lib/backup/options_spec.rb b/spec/lib/backup/options_spec.rb
index 970eea134dd..0ef10079be0 100644
--- a/spec/lib/backup/options_spec.rb
+++ b/spec/lib/backup/options_spec.rb
@@ -272,4 +272,20 @@ RSpec.describe Backup::Options, feature_category: :backup_restore do
end
end
end
+
+ describe '#skip_task?' do
+ tasks = %w[db uploads builds artifacts lfs terraform_state registry pages repositories packages ci_secure_files]
+
+ tasks.each do |task_name|
+ it "returns true when task #{task_name} is skipped" do
+ options.skippable_tasks[task_name] = true
+
+ expect(options.skip_task?(task_name)).to be(true)
+ end
+
+ it "returns false when task #{task_name} has default skip behavior" do
+ expect(options.skip_task?(task_name)).to be(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/backup/database_spec.rb b/spec/lib/backup/targets/database_spec.rb
index 7e023fda830..204ce62e32f 100644
--- a/spec/lib/backup/database_spec.rb
+++ b/spec/lib/backup/targets/database_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Backup::Database, :reestablished_active_record_base, feature_category: :backup_restore do
+RSpec.describe Backup::Targets::Database, :reestablished_active_record_base, feature_category: :backup_restore do
let(:progress) { StringIO.new }
let(:progress_output) { progress.string }
let(:backup_id) { 'some_id' }
@@ -18,7 +18,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
end
end
- before(:all) do # rubocop:disable RSpec/BeforeAll
+ before_all do
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
@@ -29,11 +29,11 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
describe '#dump', :delete do
let(:force) { true }
- subject { described_class.new(progress, force: force, options: backup_options) }
+ subject(:databases) { described_class.new(progress, force: force, options: backup_options) }
it 'creates gzipped database dumps' do
Dir.mktmpdir do |dir|
- subject.dump(dir, backup_id)
+ databases.dump(dir, backup_id)
base_models_for_backup.each_key do |database_name|
filename = database_name == 'main' ? 'database.sql.gz' : "#{database_name}_database.sql.gz"
@@ -59,7 +59,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(backup_connection).to receive(:release_snapshot!).and_call_original
end
- subject.dump(dir, backup_id)
+ databases.dump(dir, backup_id)
end
end
end
@@ -81,7 +81,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(backup_connection).not_to receive(:release_snapshot!)
end
- subject.dump(dir, backup_id)
+ databases.dump(dir, backup_id)
end
end
end
@@ -98,7 +98,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
.to receive(:new).exactly(number_of_databases).times.and_return(timeout_service)
expect(timeout_service).to receive(:restore_timeouts).exactly(number_of_databases).times
- expect { subject.dump(dir, backup_id) }.to raise_error StandardError
+ expect { databases.dump(dir, backup_id) }.to raise_error StandardError
end
end
end
@@ -111,7 +111,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
it 'will override database.yml configuration' do
# Expect an error because we can't connect to test.invalid.
expect do
- Dir.mktmpdir { |dir| subject.dump(dir, backup_id) }
+ Dir.mktmpdir { |dir| databases.dump(dir, backup_id) }
end.to raise_error(Backup::DatabaseBackupError)
expect do
@@ -129,19 +129,19 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
let(:force) { true }
let(:rake_task) { instance_double(Rake::Task, invoke: true) }
- subject { described_class.new(progress, force: force, options: backup_options) }
+ subject(:databases) { described_class.new(progress, force: force, options: backup_options) }
before do
allow(Rake::Task).to receive(:[]).with(any_args).and_return(rake_task)
- allow(subject).to receive(:pg_restore_cmd).and_return(cmd)
+ allow(databases).to receive(:pg_restore_cmd).and_return(cmd)
end
context 'when not forced' do
let(:force) { false }
it 'warns the user and waits' do
- expect(subject).to receive(:sleep)
+ expect(databases).to receive(:sleep)
if one_database_configured?
expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
@@ -149,13 +149,13 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
end
- subject.restore(backup_dir, backup_id)
+ databases.restore(backup_dir, backup_id)
expect(progress_output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
end
it 'has a pre restore warning' do
- expect(subject.pre_restore_warning).not_to be_nil
+ expect(databases.pre_restore_warning).not_to be_nil
end
end
@@ -167,7 +167,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
end
- subject.restore(backup_dir, backup_id)
+ databases.restore(backup_dir, backup_id)
expect(progress_output).to include("Restoring PostgreSQL database")
expect(progress_output).to include("[DONE]")
@@ -181,7 +181,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
it 'outputs a message about DECOMPRESS_CMD' do
expect do
- subject.restore(backup_dir, backup_id)
+ databases.restore(backup_dir, backup_id)
end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
end
end
@@ -189,7 +189,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
context 'with a corrupted .gz file' do
before do
- allow(subject).to receive(:file_name).and_return("#{backup_dir}big-image.png")
+ allow(databases).to receive(:file_name).and_return("#{backup_dir}big-image.png")
end
it 'raises a backup error' do
@@ -199,7 +199,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
end
- expect { subject.restore(backup_dir, backup_id) }.to raise_error(Backup::Error)
+ expect { databases.restore(backup_dir, backup_id) }.to raise_error(Backup::Error)
end
end
@@ -215,17 +215,17 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
end
- subject.restore(backup_dir, backup_id)
+ databases.restore(backup_dir, backup_id)
expect(progress_output).to include("ERRORS")
expect(progress_output).not_to include(noise)
expect(progress_output).to include(visible_error)
- expect(subject.post_restore_warning).not_to be_nil
+ expect(databases.post_restore_warning).not_to be_nil
end
end
context 'with PostgreSQL settings defined in the environment' do
- let(:config) { YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))['test'] }
+ let(:config) { YAML.load_file(Rails.root.join('config/database.yml'))['test'] }
before do
stub_env(ENV.to_h.merge({
@@ -244,7 +244,7 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
expect(ENV).to receive(:merge!).with(hash_including { 'PGHOST' => 'test.example.com' })
expect(ENV).not_to receive(:[]=).with('PGPASSWORD', anything)
- subject.restore(backup_dir, backup_id)
+ databases.restore(backup_dir, backup_id)
expect(ENV['PGPORT']).to eq(config['port']) if config['port']
expect(ENV['PGUSER']).to eq(config['username']) if config['username']
@@ -267,14 +267,14 @@ RSpec.describe Backup::Database, :reestablished_active_record_base, feature_cate
end
expect do
- subject.restore('db', backup_id)
+ databases.restore('db', backup_id)
end.to raise_error(Backup::Error, /Source database file does not exist/)
end
end
context 'for ci database' do
it 'ci database tolerates missing source file' do
- expect { subject.restore(backup_dir, backup_id) }.not_to raise_error
+ expect { databases.restore(backup_dir, backup_id) }.not_to raise_error
end
end
end
diff --git a/spec/lib/backup/targets/files_spec.rb b/spec/lib/backup/targets/files_spec.rb
new file mode 100644
index 00000000000..d4acd13c4cb
--- /dev/null
+++ b/spec/lib/backup/targets/files_spec.rb
@@ -0,0 +1,403 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Backup::Targets::Files, feature_category: :backup_restore do
+ let(:progress) { StringIO.new }
+ let!(:project) { create(:project) }
+ let(:backup_options) { Backup::Options.new }
+ let(:backup_basepath) { Pathname(Gitlab.config.backup.path) }
+
+ let(:status_0) { instance_double(Process::Status, success?: true, exitstatus: 0) }
+ let(:status_1) { instance_double(Process::Status, success?: false, exitstatus: 1) }
+ let(:status_2) { instance_double(Process::Status, success?: false, exitstatus: 2) }
+
+ before do
+ allow(progress).to receive(:puts)
+ allow(progress).to receive(:print)
+ allow(FileUtils).to receive(:mkdir_p).and_return(true)
+ allow(FileUtils).to receive(:mv).and_return(true)
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:realpath).with("/var/gitlab-registry").and_return("/var/gitlab-registry")
+ allow(File).to receive(:realpath).with("/var/gitlab-registry/..").and_return("/var")
+ allow(File).to receive(:realpath).with("/var/gitlab-pages").and_return("/var/gitlab-pages")
+ allow(File).to receive(:realpath).with("/var/gitlab-pages/..").and_return("/var")
+
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:progress).and_return(progress)
+ end
+ end
+
+ RSpec::Matchers.define :eq_statuslist do |expected|
+ match do |actual|
+ actual.map(&:exitstatus) == expected.map(&:exitstatus)
+ end
+
+ description do
+ 'be an Array of Process::Status with equal exitstatus against expected'
+ end
+
+ failure_message do |actual|
+ "expected #{actual} exitstatuses list to be equal #{expected} exitstatuses list"
+ end
+ end
+
+ describe '#restore' do
+ subject(:files) { described_class.new(progress, '/var/gitlab-registry', options: backup_options) }
+
+ let(:timestamp) { Time.utc(2017, 3, 22) }
+
+ around do |example|
+ travel_to(timestamp) { example.run }
+ end
+
+ describe 'folders with permission' do
+ before do
+ allow(files).to receive(:run_pipeline!).and_return([[true, true], ''])
+ allow(files).to receive(:backup_existing_files).and_return(true)
+ allow(files).to receive(:pipeline_succeeded?).and_return(true)
+ found_files = %w[/var/gitlab-registry/. /var/gitlab-registry/.. /var/gitlab-registry/sample1]
+ allow(Dir).to receive(:glob).with("/var/gitlab-registry/*", File::FNM_DOTMATCH).and_return(found_files)
+ end
+
+ it 'moves all necessary files' do
+ allow(files).to receive(:backup_existing_files).and_call_original
+
+ tmp_dir = backup_basepath.join('tmp', "registry.#{Time.now.to_i}")
+ expect(FileUtils).to receive(:mv).with(['/var/gitlab-registry/sample1'], tmp_dir)
+
+ files.restore('registry.tar.gz', 'backup_id')
+ end
+
+ it 'raises no errors' do
+ expect { files.restore('registry.tar.gz', 'backup_id') }.not_to raise_error
+ end
+
+ it 'calls tar command with unlink' do
+ expect(files).to receive(:tar).and_return('blabla-tar')
+
+ expect(files).to receive(:run_pipeline!).with(
+ ['gzip -cd', %w[blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -]],
+ any_args)
+ expect(files).to receive(:pipeline_succeeded?).and_return(true)
+
+ files.restore('registry.tar.gz', 'backup_id')
+ end
+
+ it 'raises an error on failure' do
+ expect(files).to receive(:pipeline_succeeded?).and_return(false)
+
+ expect { files.restore('registry.tar.gz', 'backup_id') }.to raise_error(/Restore operation failed:/)
+ end
+ end
+
+ describe 'folders without permissions' do
+ before do
+ allow(FileUtils).to receive(:mv).and_raise(Errno::EACCES)
+ allow(files).to receive(:run_pipeline!).and_return([[true, true], ''])
+ allow(files).to receive(:pipeline_succeeded?).and_return(true)
+ end
+
+ it 'shows error message' do
+ expect(files).to receive(:access_denied_error).with("/var/gitlab-registry")
+
+ files.restore('registry.tar.gz', 'backup_id')
+ end
+ end
+
+ describe 'folders that are a mountpoint' do
+ before do
+ allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY)
+ allow(files).to receive(:run_pipeline!).and_return([[true, true], ''])
+ allow(files).to receive(:pipeline_succeeded?).and_return(true)
+ end
+
+ it 'shows error message' do
+ expect(files).to receive(:resource_busy_error).with("/var/gitlab-registry")
+ .and_call_original
+
+ expect { files.restore('registry.tar.gz', 'backup_id') }.to raise_error(/is a mountpoint/)
+ end
+ end
+
+ describe 'with DECOMPRESS_CMD' do
+ before do
+ stub_env('DECOMPRESS_CMD', 'tee')
+ allow(files).to receive(:pipeline_succeeded?).and_return(true)
+ end
+
+ it 'passes through tee instead of gzip' do
+ expect(files).to receive(:run_pipeline!).with(['tee', anything], any_args).and_return([[true, true], ''])
+
+ expect do
+ files.restore('registry.tar.gz', 'backup_id')
+ end.to output(/Using custom DECOMPRESS_CMD 'tee'/).to_stdout
+ end
+ end
+ end
+
+ describe '#dump' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-pages', excludes: ['@pages.tmp'], options: backup_options)
+ end
+
+ before do
+ allow(files).to receive(:run_pipeline!).and_return([[true, true], ''])
+ allow(files).to receive(:pipeline_succeeded?).and_return(true)
+ end
+
+ it 'raises no errors' do
+ expect { files.dump('registry.tar.gz', 'backup_id') }.not_to raise_error
+ end
+
+ it 'excludes tmp dirs from archive' do
+ expect(files).to receive(:tar).and_return('blabla-tar')
+
+ expect(files).to receive(:run_pipeline!).with(
+ [%w[blabla-tar --exclude=lost+found --exclude=./@pages.tmp -C /var/gitlab-pages -cf - .], 'gzip -c -1'],
+ any_args)
+ files.dump('registry.tar.gz', 'backup_id')
+ end
+
+ it 'raises an error on failure' do
+ allow(files).to receive(:run_pipeline!).and_return([[true, true], ''])
+ expect(files).to receive(:pipeline_succeeded?).and_return(false)
+
+ expect do
+ files.dump('registry.tar.gz', 'backup_id')
+ end.to raise_error(/Failed to create compressed file/)
+ end
+
+ describe 'with STRATEGY=copy' do
+ before do
+ stub_env('STRATEGY', 'copy')
+ allow(files).to receive(:backup_basepath).and_return(Pathname('/var/gitlab-backup'))
+ allow(File).to receive(:realpath).with('/var/gitlab-backup').and_return('/var/gitlab-backup')
+ end
+
+ it 'excludes tmp dirs from rsync' do
+ cmd_args = %w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp
+ /var/gitlab-pages /var/gitlab-backup]
+ expect(Gitlab::Popen).to receive(:popen).with(cmd_args).and_return(['', 0])
+
+ files.dump('registry.tar.gz', 'backup_id')
+ end
+
+ it 'retries if rsync fails due to vanishing files' do
+ cmd_args = %w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp
+ /var/gitlab-pages /var/gitlab-backup]
+ expect(Gitlab::Popen).to receive(:popen).with(cmd_args).and_return(['rsync failed', 24], ['', 0])
+
+ expect do
+ files.dump('registry.tar.gz', 'backup_id')
+ end.to output(/files vanished during rsync, retrying/).to_stdout
+ end
+
+ it 'raises an error and outputs an error message if rsync failed' do
+ cmd_args = %w[rsync -a --delete --exclude=lost+found --exclude=/gitlab-pages/@pages.tmp
+ /var/gitlab-pages /var/gitlab-backup]
+ allow(Gitlab::Popen).to receive(:popen).with(cmd_args).and_return(['rsync failed', 1])
+
+ expect do
+ files.dump('registry.tar.gz', 'backup_id')
+ end.to output(/rsync failed/).to_stdout
+ .and raise_error(/Failed to create compressed file/)
+ end
+ end
+
+ describe 'with COMPRESS_CMD' do
+ before do
+ stub_env('COMPRESS_CMD', 'tee')
+ end
+
+ it 'passes through tee instead of gzip' do
+ expect(files).to receive(:run_pipeline!).with([anything, 'tee'], any_args)
+ expect do
+ files.dump('registry.tar.gz', 'backup_id')
+ end.to output(/Using custom COMPRESS_CMD 'tee'/).to_stdout
+ end
+ end
+
+ context 'when GZIP_RSYNCABLE is "yes"' do
+ before do
+ stub_env('GZIP_RSYNCABLE', 'yes')
+ end
+
+ it 'gzips the files with rsyncable option' do
+ expect(files).to receive(:run_pipeline!).with([anything, 'gzip --rsyncable -c -1'], any_args)
+ files.dump('registry.tar.gz', 'backup_id')
+ end
+ end
+
+ context 'when GZIP_RSYNCABLE is not set' do
+ it 'gzips the files without the rsyncable option' do
+ expect(files).to receive(:run_pipeline!).with([anything, 'gzip -c -1'], any_args)
+ files.dump('registry.tar.gz', 'backup_id')
+ end
+ end
+ end
+
+ describe '#exclude_dirs' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-pages', excludes: ['@pages.tmp'], options: backup_options)
+ end
+
+ it 'prepends a leading dot slash to tar excludes' do
+ expect(files.exclude_dirs(:tar)).to eq(%w[--exclude=lost+found --exclude=./@pages.tmp])
+ end
+
+ it 'prepends a leading slash and app_files_dir basename to rsync excludes' do
+ expect(files.exclude_dirs(:rsync)).to eq(%w[--exclude=lost+found --exclude=/gitlab-pages/@pages.tmp])
+ end
+ end
+
+ describe '#run_pipeline!' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-registry', options: backup_options)
+ end
+
+ it 'executes an Open3.pipeline for cmd_list' do
+ expect(Open3).to receive(:pipeline).with(%w[whew command], %w[another cmd], any_args)
+
+ files.run_pipeline!([%w[whew command], %w[another cmd]])
+ end
+
+ it 'returns an empty output on success pipeline' do
+ expect(files.run_pipeline!(%w[true true])[1]).to eq('')
+ end
+
+ it 'returns the stderr for failed pipeline' do
+ expect(
+ files.run_pipeline!(['echo OMG: failed command present 1>&2; false', 'true'])[1]
+ ).to match(/OMG: failed/)
+ end
+
+ it 'returns the success status list on success pipeline' do
+ expect(
+ files.run_pipeline!(%w[true true])[0]
+ ).to eq_statuslist([status_0, status_0])
+ end
+
+ it 'returns the failed status in status list for failed commands in pipeline' do
+ expect(files.run_pipeline!(%w[false true true])[0]).to eq_statuslist([status_1, status_0, status_0])
+ expect(files.run_pipeline!(%w[true false true])[0]).to eq_statuslist([status_0, status_1, status_0])
+ expect(files.run_pipeline!(%w[false false true])[0]).to eq_statuslist([status_1, status_1, status_0])
+ expect(files.run_pipeline!(%w[false true false])[0]).to eq_statuslist([status_1, status_0, status_1])
+ expect(files.run_pipeline!(%w[false false false])[0]).to eq_statuslist([status_1, status_1, status_1])
+ end
+ end
+
+ describe '#pipeline_succeeded?' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-registry', options: backup_options)
+ end
+
+ it 'returns true if both tar and gzip succeeeded' do
+ expect(
+ files.pipeline_succeeded?(tar_status: status_0, compress_status: status_0, output: 'any_output')
+ ).to be_truthy
+ end
+
+ it 'returns false if gzip failed' do
+ expect(
+ files.pipeline_succeeded?(tar_status: status_1, compress_status: status_1, output: 'any_output')
+ ).to be_falsey
+ end
+
+ context 'if gzip succeeded and tar failed non-critically' do
+ before do
+ allow(files).to receive(:tar_ignore_non_success?).and_return(true)
+ end
+
+ it 'returns true' do
+ expect(
+ files.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
+ ).to be_truthy
+ end
+ end
+
+ context 'if gzip succeeded and tar failed in other cases' do
+ before do
+ allow(files).to receive(:tar_ignore_non_success?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(
+ files.pipeline_succeeded?(tar_status: status_1, compress_status: status_0, output: 'any_output')
+ ).to be_falsey
+ end
+ end
+ end
+
+ describe '#tar_ignore_non_success?' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-registry', options: backup_options)
+ end
+
+ context 'if `tar` command exits with 1 exitstatus' do
+ it 'returns true' do
+ expect(
+ files.tar_ignore_non_success?(1, 'any_output')
+ ).to be_truthy
+ end
+
+ it 'outputs a warning' do
+ expect do
+ files.tar_ignore_non_success?(1, 'any_output')
+ end.to output(/Ignoring tar exit status 1/).to_stdout
+ end
+ end
+
+ context 'if `tar` command exits with 2 exitstatus with non-critical warning' do
+ before do
+ allow(files).to receive(:noncritical_warning?).and_return(true)
+ end
+
+ it 'returns true' do
+ expect(
+ files.tar_ignore_non_success?(2, 'any_output')
+ ).to be_truthy
+ end
+
+ it 'outputs a warning' do
+ expect do
+ files.tar_ignore_non_success?(2, 'any_output')
+ end.to output(/Ignoring non-success exit status/).to_stdout
+ end
+ end
+
+ context 'if `tar` command exits with any other unlisted error' do
+ before do
+ allow(files).to receive(:noncritical_warning?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(
+ files.tar_ignore_non_success?(2, 'any_output')
+ ).to be_falsey
+ end
+ end
+ end
+
+ describe '#noncritical_warning?' do
+ subject(:files) do
+ described_class.new(progress, '/var/gitlab-registry', options: backup_options)
+ end
+
+ it 'returns true if given text matches noncritical warnings list' do
+ expect(
+ files.noncritical_warning?('tar: .: Cannot mkdir: No such file or directory')
+ ).to be_truthy
+
+ expect(
+ files.noncritical_warning?('gtar: .: Cannot mkdir: No such file or directory')
+ ).to be_truthy
+ end
+
+ it 'returns false otherwize' do
+ expect(
+ files.noncritical_warning?('unknown message')
+ ).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/backup/repositories_spec.rb b/spec/lib/backup/targets/repositories_spec.rb
index e63d321495e..0f203e114b2 100644
--- a/spec/lib/backup/repositories_spec.rb
+++ b/spec/lib/backup/targets/repositories_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe Backup::Repositories, feature_category: :backup_restore do
- let(:progress) { spy(:stdout) }
- let(:strategy) { spy(:strategy) }
+RSpec.describe Backup::Targets::Repositories, feature_category: :backup_restore do
+ let(:progress) { instance_double(StringIO, puts: nil, print: nil) }
+ let(:strategy) { instance_double(Backup::GitalyBackup, start: nil, enqueue: nil, finish!: nil) }
let(:storages) { [] }
let(:paths) { [] }
let(:skip_paths) { [] }
@@ -12,7 +12,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
let(:backup_id) { 'backup_id' }
let(:backup_options) { Backup::Options.new }
- subject do
+ subject(:repositories) do
described_class.new(
progress,
strategy: strategy,
@@ -31,25 +31,26 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
project_snippet = create(:project_snippet, :repository, project: project)
personal_snippet = create(:personal_snippet, :repository, author: project.first_owner)
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:enqueue).with(project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:finish!)
end
end
- context 'hashed storage' do
+ context 'with hashed storage' do
let_it_be(:project) { create(:project_with_design, :repository) }
it_behaves_like 'creates repository bundles'
end
- context 'legacy storage' do
+ context 'with legacy storage' do
let_it_be(:project) { create(:project_with_design, :repository, :legacy_storage) }
it_behaves_like 'creates repository bundles'
@@ -59,19 +60,19 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
it 'enqueue_project raises an error' do
allow(strategy).to receive(:enqueue).with(anything, Gitlab::GlRepository::PROJECT).and_raise(IOError)
- expect { subject.dump(destination, backup_id) }.to raise_error(IOError)
+ expect { repositories.dump(destination, backup_id) }.to raise_error(IOError)
end
it 'project query raises an error' do
allow(Project).to receive_message_chain(:includes, :find_each).and_raise(ActiveRecord::StatementTimeout)
- expect { subject.dump(destination, backup_id) }.to raise_error(ActiveRecord::StatementTimeout)
+ expect { repositories.dump(destination, backup_id) }.to raise_error(ActiveRecord::StatementTimeout)
end
end
it 'avoids N+1 database queries' do
control = ActiveRecord::QueryRecorder.new do
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
end
create_list(:project, 2, :repository)
@@ -82,7 +83,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
# for each project.
# We are using 2 projects here.
expect do
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
end.not_to exceed_query_limit(control).with_threshold(2)
end
@@ -102,7 +103,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
excluded_personal_snippet.track_snippet_repository('test_second_storage')
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
@@ -110,7 +111,8 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
@@ -118,7 +120,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
describe 'paths' do
let_it_be(:project) { create(:project_with_design, :repository) }
- context 'project path' do
+ context 'with a project path' do
let(:paths) { [project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
@@ -126,7 +128,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
@@ -134,12 +136,13 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
- context 'group path' do
+ context 'with a group path' do
let(:paths) { [project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
@@ -147,7 +150,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
@@ -155,7 +158,8 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
@@ -165,14 +169,14 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
let_it_be(:project) { create(:project_with_design, :repository) }
let_it_be(:excluded_project) { create(:project, :repository) }
- context 'project path' do
+ context 'with a project path' do
let(:skip_paths) { [excluded_project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
included_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
@@ -180,19 +184,20 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
expect(strategy).to have_received(:enqueue).with(included_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
- context 'group path' do
+ context 'with a group path' do
let(:skip_paths) { [excluded_project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
included_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.dump(destination, backup_id)
+ repositories.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
@@ -200,7 +205,8 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
expect(strategy).to have_received(:enqueue).with(included_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
@@ -214,23 +220,25 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: project.first_owner) }
it 'calls enqueue for each repository type', :aggregate_failures do
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default], backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default],
+ backup_id: backup_id)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:enqueue).with(project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:finish!)
end
- context 'restoring object pools' do
+ context 'when restoring object pools' do
it 'schedules restoring of the pool', :sidekiq_might_not_need_inline do
pool_repository = create(:pool_repository, :failed)
pool_repository.delete_object_pool
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
pool_repository.reload
expect(pool_repository).not_to be_failed
@@ -241,14 +249,14 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
pool_repository = create(:pool_repository, state: :obsolete)
pool_repository.update_column(:source_project_id, nil)
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
pool_repository.reload
expect(pool_repository).to be_obsolete
end
end
- context 'storages' do
+ context 'for storages' do
let(:storages) { %w[default] }
before do
@@ -262,21 +270,23 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
excluded_personal_snippet.track_snippet_repository('test_second_storage')
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default], backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default],
+ backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
- context 'paths' do
- context 'project path' do
+ context 'for paths' do
+ context 'when project path' do
let(:paths) { [project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
@@ -284,20 +294,22 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil, backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil,
+ backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
- context 'group path' do
+ context 'with a group path' do
let(:paths) { [project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
@@ -305,59 +317,65 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil, backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil,
+ backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
end
- context 'skip_paths' do
+ context 'for skip_paths' do
let_it_be(:excluded_project) { create(:project, :repository) }
- context 'project path' do
+ context 'with a project path' do
let(:skip_paths) { [excluded_project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
included_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default], backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default],
+ backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(included_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
- context 'group path' do
+ context 'with a group path' do
let(:skip_paths) { [excluded_project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
included_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
- subject.restore(destination, backup_id)
+ repositories.restore(destination, backup_id)
- expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default], backup_id: backup_id)
+ expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default],
+ backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(included_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
- expect(strategy).to have_received(:enqueue).with(project.design_management_repository, Gitlab::GlRepository::DESIGN)
+ expect(strategy).to have_received(:enqueue).with(project.design_management_repository,
+ Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
diff --git a/spec/lib/backup/targets/target_spec.rb b/spec/lib/backup/targets/target_spec.rb
new file mode 100644
index 00000000000..f69d95fb382
--- /dev/null
+++ b/spec/lib/backup/targets/target_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Backup::Targets::Target, feature_category: :backup_restore do
+ let(:progress) { StringIO.new }
+ let(:backup_options) { build(:backup_options) }
+
+ subject(:target) { described_class.new(progress, options: backup_options) }
+
+ describe '#options' do
+ it 'has an accessor for Backup::Options' do
+ expect(target.options).to be_a(Backup::Options)
+ end
+ end
+
+ describe '#dump' do
+ it 'must be implemented by the subclass' do
+ expect { target.dump('some/path', 'backup_id') }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#restore' do
+ it 'must be implemented by the subclass' do
+ expect { target.restore('some/path', 'backup_id') }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#pre_restore_warning' do
+ it { respond_to :pre_restore_warning }
+ end
+
+ describe '#pos_restore_warning' do
+ it { respond_to :pos_restore_warning }
+ end
+end
diff --git a/spec/lib/backup/task_spec.rb b/spec/lib/backup/task_spec.rb
deleted file mode 100644
index 5ded16cd52b..00000000000
--- a/spec/lib/backup/task_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Backup::Task, feature_category: :backup_restore do
- let(:progress) { StringIO.new }
- let(:backup_options) { build(:backup_options) }
-
- subject { described_class.new(progress, options: backup_options) }
-
- describe '#dump' do
- it 'must be implemented by the subclass' do
- expect { subject.dump('some/path', 'backup_id') }.to raise_error(NotImplementedError)
- end
- end
-
- describe '#restore' do
- it 'must be implemented by the subclass' do
- expect { subject.restore('some/path', 'backup_id') }.to raise_error(NotImplementedError)
- end
- end
-end