Commit 9ab97572 authored by Valery Sizov's avatar Valery Sizov Committed by Nick Thomas

Resolve "Geo: Leftover temporary directories from failed clones"

parent e563e820
...@@ -54,6 +54,7 @@ module Geo ...@@ -54,6 +54,7 @@ module Geo
def fetch_repository(redownload) def fetch_repository(redownload)
log_info("Trying to fetch #{type}") log_info("Trying to fetch #{type}")
clean_up_temporary_repository
update_registry!(started_at: DateTime.now) update_registry!(started_at: DateTime.now)
if redownload if redownload
...@@ -157,17 +158,13 @@ module Geo ...@@ -157,17 +158,13 @@ module Geo
registry.public_send("last_#{type}_synced_at") # rubocop:disable GitlabSecurity/PublicSend registry.public_send("last_#{type}_synced_at") # rubocop:disable GitlabSecurity/PublicSend
end end
def random_disk_path(prefix)
random_string = SecureRandom.hex(7)
"#{repository.disk_path}_#{prefix}#{random_string}"
end
def disk_path_temp def disk_path_temp
@disk_path_temp ||= random_disk_path('') # We use "@" as it's not allowed to use it in a group or project name
@disk_path_temp ||= "@geo-temporary/#{repository.disk_path}"
end end
def deleted_disk_path_temp def deleted_disk_path_temp
@deleted_path ||= "#{repository.disk_path}+failed-geo-sync" @deleted_path ||= "@failed-geo-sync/#{repository.disk_path}"
end end
def build_temporary_repository def build_temporary_repository
...@@ -175,16 +172,17 @@ module Geo ...@@ -175,16 +172,17 @@ module Geo
raise Gitlab::Shell::Error, 'Can not create a temporary repository' raise Gitlab::Shell::Error, 'Can not create a temporary repository'
end end
log_info( log_info("Created temporary repository")
'Created temporary repository',
temp_path: disk_path_temp
)
repository.clone.tap { |repo| repo.disk_path = disk_path_temp } repository.clone.tap { |repo| repo.disk_path = disk_path_temp }
end end
def clean_up_temporary_repository def clean_up_temporary_repository
gitlab_shell.remove_repository(project.repository_storage_path, disk_path_temp) exists = gitlab_shell.exists?(project.repository_storage_path, disk_path_temp)
if exists && !gitlab_shell.remove_repository(project.repository_storage_path, disk_path_temp)
raise Gitlab::Shell::Error, "Temporary #{type} can not been removed"
end
end end
def set_temp_repository_as_main def set_temp_repository_as_main
...@@ -199,6 +197,9 @@ module Geo ...@@ -199,6 +197,9 @@ module Geo
# Remove the deleted path in case it exists, but it may not be there # Remove the deleted path in case it exists, but it may not be there
gitlab_shell.remove_repository(project.repository_storage_path, deleted_disk_path_temp) gitlab_shell.remove_repository(project.repository_storage_path, deleted_disk_path_temp)
# Make sure we have a namespace directory
gitlab_shell.add_namespace(project.repository_storage_path, deleted_disk_path_temp)
if project.repository_exists? && !gitlab_shell.mv_repository(project.repository_storage_path, repository.disk_path, deleted_disk_path_temp) if project.repository_exists? && !gitlab_shell.mv_repository(project.repository_storage_path, repository.disk_path, deleted_disk_path_temp)
raise Gitlab::Shell::Error, 'Can not move original repository out of the way' raise Gitlab::Shell::Error, 'Can not move original repository out of the way'
end end
......
---
title: 'Fix Geo: Leftover temporary directories from failed clones'
merge_request:
author:
type: fixed
...@@ -129,6 +129,12 @@ describe Geo::RepositorySyncService do ...@@ -129,6 +129,12 @@ describe Geo::RepositorySyncService do
end end
context 'tracking database' do context 'tracking database' do
context 'temporary repositories' do
include_examples 'cleans temporary repositories' do
let(:repository) { project.repository }
end
end
it 'creates a new registry if does not exists' do it 'creates a new registry if does not exists' do
expect { subject.execute }.to change(Geo::ProjectRegistry, :count).by(1) expect { subject.execute }.to change(Geo::ProjectRegistry, :count).by(1)
end end
...@@ -236,7 +242,7 @@ describe Geo::RepositorySyncService do ...@@ -236,7 +242,7 @@ describe Geo::RepositorySyncService do
expect(subject).to receive(:sync_repository).with(true).and_call_original expect(subject).to receive(:sync_repository).with(true).and_call_original
expect(subject.gitlab_shell).to receive(:mv_repository).exactly(2).times.and_call_original expect(subject.gitlab_shell).to receive(:mv_repository).exactly(2).times.and_call_original
expect(subject.gitlab_shell).to receive(:remove_repository).exactly(3).times.and_call_original expect(subject.gitlab_shell).to receive(:remove_repository).exactly(2).times.and_call_original
subject.execute subject.execute
...@@ -260,6 +266,20 @@ describe Geo::RepositorySyncService do ...@@ -260,6 +266,20 @@ describe Geo::RepositorySyncService do
subject.execute subject.execute
end end
it 'cleans temporary repo after redownload' do
create(
:geo_project_registry,
project: project,
repository_retry_count: Geo::BaseSyncService::RETRY_BEFORE_REDOWNLOAD - 1,
force_to_redownload_repository: true
)
expect(subject).to receive(:fetch_geo_mirror)
expect(subject).to receive(:clean_up_temporary_repository).twice
subject.execute
end
it 'successfully redownloads the repository even if the retry time exceeds max value' do it 'successfully redownloads the repository even if the retry time exceeds max value' do
timestamp = Time.now.utc timestamp = Time.now.utc
registry = create( registry = create(
......
...@@ -108,6 +108,12 @@ RSpec.describe Geo::WikiSyncService do ...@@ -108,6 +108,12 @@ RSpec.describe Geo::WikiSyncService do
end end
context 'tracking database' do context 'tracking database' do
context 'temporary repositories' do
include_examples 'cleans temporary repositories' do
let(:repository) { project.wiki.repository }
end
end
it 'creates a new registry if does not exists' do it 'creates a new registry if does not exists' do
expect { subject.execute }.to change(Geo::ProjectRegistry, :count).by(1) expect { subject.execute }.to change(Geo::ProjectRegistry, :count).by(1)
end end
......
...@@ -29,3 +29,21 @@ shared_examples 'geo base sync execution' do ...@@ -29,3 +29,21 @@ shared_examples 'geo base sync execution' do
end end
end end
end end
shared_examples 'cleans temporary repositories' do
context 'there is a leftover repository' do
let(:temp_repo_path) { "@geo-temporary/#{repository.disk_path}" }
it 'removes leftover repository' do
gitlab_shell = instance_double('Gitlab::Shell')
allow(subject).to receive(:gitlab_shell).and_return(gitlab_shell)
allow(subject).to receive(:fetch_geo_mirror)
expect(gitlab_shell).to receive(:exists?).and_return(true)
expect(gitlab_shell).to receive(:remove_repository).with(project.repository_storage_path, temp_repo_path)
subject.execute
end
end
end
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