debian-mirror-gitlab/spec/lib/backup/repositories_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

306 lines
14 KiB
Ruby
Raw Normal View History

2021-01-03 14:25:43 +05:30
# frozen_string_literal: true
require 'spec_helper'
2023-06-20 00:43:36 +05:30
RSpec.describe Backup::Repositories, feature_category: :backup_restore do
2021-09-04 01:27:46 +05:30
let(:progress) { spy(:stdout) }
2022-06-21 17:19:12 +05:30
let(:strategy) { spy(:strategy) }
2022-07-16 23:28:13 +05:30
let(:storages) { [] }
2022-07-23 23:45:48 +05:30
let(:paths) { [] }
2022-05-07 20:08:51 +05:30
let(:destination) { 'repositories' }
2022-06-21 17:19:12 +05:30
let(:backup_id) { 'backup_id' }
2022-04-04 11:22:00 +05:30
subject do
described_class.new(
progress,
2022-07-16 23:28:13 +05:30
strategy: strategy,
2022-07-23 23:45:48 +05:30
storages: storages,
paths: paths
2022-04-04 11:22:00 +05:30
)
end
2021-01-03 14:25:43 +05:30
describe '#dump' do
let_it_be(:projects) { create_list(:project, 5, :repository) }
RSpec.shared_examples 'creates repository bundles' do
2021-09-04 01:27:46 +05:30
it 'calls enqueue for each repository type', :aggregate_failures do
2021-01-03 14:25:43 +05:30
project_snippet = create(:project_snippet, :repository, project: project)
2022-04-04 11:22:00 +05:30
personal_snippet = create(:personal_snippet, :repository, author: project.first_owner)
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
subject.dump(destination, backup_id)
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
2021-09-04 01:27:46 +05:30
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, 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)
2022-03-02 08:16:31 +05:30
expect(strategy).to have_received(:finish!)
2021-01-03 14:25:43 +05:30
end
end
context 'hashed storage' do
let_it_be(:project) { create(:project, :repository) }
it_behaves_like 'creates repository bundles'
end
context 'legacy storage' do
let_it_be(:project) { create(:project, :repository, :legacy_storage) }
it_behaves_like 'creates repository bundles'
end
2022-06-21 17:19:12 +05:30
describe 'command failure' do
it 'enqueue_project raises an error' do
allow(strategy).to receive(:enqueue).with(anything, Gitlab::GlRepository::PROJECT).and_raise(IOError)
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
expect { subject.dump(destination, backup_id) }.to raise_error(IOError)
2021-01-03 14:25:43 +05:30
end
2022-06-21 17:19:12 +05:30
it 'project query raises an error' do
allow(Project).to receive_message_chain(:includes, :find_each).and_raise(ActiveRecord::StatementTimeout)
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
expect { subject.dump(destination, backup_id) }.to raise_error(ActiveRecord::StatementTimeout)
2021-01-03 14:25:43 +05:30
end
end
2022-06-21 17:19:12 +05:30
it 'avoids N+1 database queries' do
control_count = ActiveRecord::QueryRecorder.new do
subject.dump(destination, backup_id)
end.count
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
create_list(:project, 2, :repository)
2022-07-16 23:28:13 +05:30
create_list(:snippet, 2, :repository)
2021-01-03 14:25:43 +05:30
2022-06-21 17:19:12 +05:30
expect do
subject.dump(destination, backup_id)
end.not_to exceed_query_limit(control_count)
2021-01-03 14:25:43 +05:30
end
2022-07-16 23:28:13 +05:30
describe 'storages' do
let(:storages) { %w{default} }
let_it_be(:project) { create(:project, :repository) }
before do
stub_storage_settings('test_second_storage' => {
'gitaly_address' => Gitlab.config.repositories.storages.default.gitaly_address,
'path' => TestEnv::SECOND_STORAGE_PATH
})
end
it 'calls enqueue for all repositories on the specified storage', :aggregate_failures do
excluded_project = create(:project, :repository, repository_storage: 'test_second_storage')
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_project_snippet.track_snippet_repository('test_second_storage')
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)
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)
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
2022-07-23 23:45:48 +05:30
describe 'paths' do
let_it_be(:project) { create(:project, :repository) }
context 'project path' do
let(:paths) { [project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
excluded_project = create(:project, :repository)
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)
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)
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
context 'group path' do
let(:paths) { [project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
excluded_project = create(:project, :repository)
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)
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)
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
end
2021-01-03 14:25:43 +05:30
end
describe '#restore' do
2022-07-16 23:28:13 +05:30
let_it_be(:project) { create(:project, :repository) }
2023-07-09 08:55:56 +05:30
2022-07-16 23:28:13 +05:30
let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: project.first_owner) }
let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: project.first_owner) }
2021-01-03 14:25:43 +05:30
2021-09-04 01:27:46 +05:30
it 'calls enqueue for each repository type', :aggregate_failures do
2022-05-07 20:08:51 +05:30
subject.restore(destination)
2021-01-03 14:25:43 +05:30
2023-06-20 00:43:36 +05:30
expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default])
2021-09-04 01:27:46 +05:30
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, 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)
2022-03-02 08:16:31 +05:30
expect(strategy).to have_received(:finish!)
2021-01-03 14:25:43 +05:30
end
context '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
2022-05-07 20:08:51 +05:30
subject.restore(destination)
2021-01-03 14:25:43 +05:30
pool_repository.reload
expect(pool_repository).not_to be_failed
expect(pool_repository.object_pool.exists?).to be(true)
end
2021-04-17 20:07:23 +05:30
2022-07-23 23:45:48 +05:30
it 'skips pools when no source project is found', :sidekiq_might_not_need_inline do
2021-04-17 20:07:23 +05:30
pool_repository = create(:pool_repository, state: :obsolete)
pool_repository.update_column(:source_project_id, nil)
2022-05-07 20:08:51 +05:30
subject.restore(destination)
2021-04-17 20:07:23 +05:30
pool_repository.reload
expect(pool_repository).to be_obsolete
end
2021-01-03 14:25:43 +05:30
end
2021-09-04 01:27:46 +05:30
context 'cleanup snippets' do
2021-01-03 14:25:43 +05:30
before do
2021-09-04 01:27:46 +05:30
error_response = ServiceResponse.error(message: "Repository has more than one branch")
allow(Snippets::RepositoryValidationService).to receive_message_chain(:new, :execute).and_return(error_response)
2021-01-03 14:25:43 +05:30
end
2021-09-04 01:27:46 +05:30
it 'shows the appropriate error' do
2022-05-07 20:08:51 +05:30
subject.restore(destination)
2021-01-03 14:25:43 +05:30
2021-09-04 01:27:46 +05:30
expect(progress).to have_received(:puts).with("Snippet #{personal_snippet.full_path} can't be restored: Repository has more than one branch")
expect(progress).to have_received(:puts).with("Snippet #{project_snippet.full_path} can't be restored: Repository has more than one branch")
2021-01-03 14:25:43 +05:30
end
2021-09-04 01:27:46 +05:30
it 'removes the snippets from the DB' do
2022-05-07 20:08:51 +05:30
expect { subject.restore(destination) }.to change(PersonalSnippet, :count).by(-1)
2021-09-04 01:27:46 +05:30
.and change(ProjectSnippet, :count).by(-1)
.and change(SnippetRepository, :count).by(-2)
end
2021-01-03 14:25:43 +05:30
2021-09-04 01:27:46 +05:30
it 'removes the repository from disk' do
gitlab_shell = Gitlab::Shell.new
shard_name = personal_snippet.repository.shard
path = personal_snippet.disk_path + '.git'
2021-01-03 14:25:43 +05:30
2022-05-07 20:08:51 +05:30
subject.restore(destination)
2021-01-03 14:25:43 +05:30
2021-09-04 01:27:46 +05:30
expect(gitlab_shell.repository_exists?(shard_name, path)).to eq false
2021-01-03 14:25:43 +05:30
end
end
2022-07-16 23:28:13 +05:30
context 'storages' do
let(:storages) { %w{default} }
before do
stub_storage_settings('test_second_storage' => {
'gitaly_address' => Gitlab.config.repositories.storages.default.gitaly_address,
'path' => TestEnv::SECOND_STORAGE_PATH
})
end
it 'calls enqueue for all repositories on the specified storage', :aggregate_failures do
excluded_project = create(:project, :repository, repository_storage: 'test_second_storage')
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_project_snippet.track_snippet_repository('test_second_storage')
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
excluded_personal_snippet.track_snippet_repository('test_second_storage')
subject.restore(destination)
2023-06-20 00:43:36 +05:30
expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: %w[default])
2022-07-16 23:28:13 +05:30
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
2022-07-23 23:45:48 +05:30
context 'paths' do
context 'project path' do
let(:paths) { [project.full_path] }
it 'calls enqueue for all repositories on the specified project', :aggregate_failures do
excluded_project = create(:project, :repository)
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)
2023-06-20 00:43:36 +05:30
expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil)
2022-07-23 23:45:48 +05:30
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
context 'group path' do
let(:paths) { [project.namespace.full_path] }
it 'calls enqueue for all repositories on all descendant projects', :aggregate_failures do
excluded_project = create(:project, :repository)
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)
2023-06-20 00:43:36 +05:30
expect(strategy).to have_received(:start).with(:restore, destination, remove_all_repositories: nil)
2022-07-23 23:45:48 +05:30
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, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
end
2021-01-03 14:25:43 +05:30
end
end