Commit c2ae03e2 authored by Gabriel Mazetto's avatar Gabriel Mazetto

Make Hashed Storage rollback resilient to exceptions

parent daf0a766
...@@ -21,14 +21,32 @@ module Projects ...@@ -21,14 +21,32 @@ module Projects
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository] project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
end end
project.repository_read_only = false project.transaction do
project.save!(validate: false) project.save!(validate: false)
project.set_repository_writable!
if result && block_given?
yield
end end
result result
rescue Gitlab::Git::CommandError => e
logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). Git operation failed: #{e.inspect}")
rollback_migration!
false
rescue OpenSSL::Cipher::CipherError => e
logger.error("Repository #{project.full_path} failed to rollback (PROJECT_ID=#{project.id}). There is a problem with encrypted attributes: #{e.inspect}")
rollback_migration!
false
end
private
def rollback_migration!
rollback_folder_move
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
project.set_repository_writable!
end end
end end
end end
......
---
title: "Hashed Storage: make migration and rollback resilient to exceptions"
merge_request: 46178
author:
type: fixed
...@@ -77,6 +77,42 @@ RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab ...@@ -77,6 +77,42 @@ RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab
end end
end end
context 'when exception happens' do
it 'handles OpenSSL::Cipher::CipherError' do
expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError)
expect { service.execute }.not_to raise_exception
end
it 'ensures rollback when OpenSSL::Cipher::CipherError' do
expect(project).to receive(:ensure_runners_token).and_raise(OpenSSL::Cipher::CipherError)
expect(service).to receive(:rollback_folder_move).and_call_original
service.execute
project.reload
expect(project.hashed_storage?(:repository)).to be_truthy
expect(project.repository_read_only?).to be_falsey
end
it 'handles Gitlab::Git::CommandError' do
expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError)
expect { service.execute }.not_to raise_exception
end
it 'ensures rollback when Gitlab::Git::CommandError' do
expect(project).to receive(:write_repository_config).and_raise(Gitlab::Git::CommandError)
expect(service).to receive(:rollback_folder_move).and_call_original
service.execute
project.reload
expect(project.hashed_storage?(:repository)).to be_truthy
expect(project.repository_read_only?).to be_falsey
end
end
context 'when one move fails' do context 'when one move fails' do
it 'rolls repositories back to original name' do it 'rolls repositories back to original name' do
allow(service).to receive(:move_repository).and_call_original allow(service).to receive(:move_repository).and_call_original
......
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