diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-28 03:08:39 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-28 03:08:39 +0000 |
commit | 5cef625594aedbac12011d870719fe81a1587a98 (patch) | |
tree | 147d465fb4275ab2d14be99ed58888ca23e10111 /app | |
parent | ee7de3a24d62376916d78649d7e477a184b2e203 (diff) | |
download | gitlab-ce-5cef625594aedbac12011d870719fe81a1587a98.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/services/projects/destroy_rollback_service.rb | 31 | ||||
-rw-r--r-- | app/services/projects/destroy_service.rb | 98 | ||||
-rw-r--r-- | app/services/projects/overwrite_project_service.rb | 2 | ||||
-rw-r--r-- | app/services/repositories/base_service.rb | 52 | ||||
-rw-r--r-- | app/services/repositories/destroy_rollback_service.rb | 19 | ||||
-rw-r--r-- | app/services/repositories/destroy_service.rb | 28 | ||||
-rw-r--r-- | app/services/repositories/shell_destroy_service.rb | 15 |
7 files changed, 158 insertions, 87 deletions
diff --git a/app/services/projects/destroy_rollback_service.rb b/app/services/projects/destroy_rollback_service.rb new file mode 100644 index 00000000000..7f0ca63a406 --- /dev/null +++ b/app/services/projects/destroy_rollback_service.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Projects + class DestroyRollbackService < BaseService + include Gitlab::ShellAdapter + + def execute + return unless project + + Projects::ForksCountService.new(project).delete_cache + + unless rollback_repository(project.repository) + raise_error(s_('DeleteProject|Failed to restore project repository. Please contact the administrator.')) + end + + unless rollback_repository(project.wiki.repository) + raise_error(s_('DeleteProject|Failed to restore wiki repository. Please contact the administrator.')) + end + end + + private + + def rollback_repository(repository) + return true unless repository + + result = Repositories::DestroyRollbackService.new(repository).execute + + result[:status] == :success + end + end +end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index cbed794f92e..066d1f1ca72 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -6,9 +6,6 @@ module Projects DestroyError = Class.new(StandardError) - DELETED_FLAG = '+deleted' - REPO_REMOVAL_DELAY = 5.minutes.to_i - def async_execute project.update_attribute(:pending_delete, true) @@ -18,7 +15,7 @@ module Projects schedule_stale_repos_removal job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params) - Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") # rubocop:disable Gitlab/RailsLogger + log_info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}") end def execute @@ -48,82 +45,34 @@ module Projects raise end - def attempt_repositories_rollback - return unless @project - - flush_caches(@project) - - unless rollback_repository(removal_path(repo_path), repo_path) - raise_error(s_('DeleteProject|Failed to restore project repository. Please contact the administrator.')) - end - - unless rollback_repository(removal_path(wiki_path), wiki_path) - raise_error(s_('DeleteProject|Failed to restore wiki repository. Please contact the administrator.')) - end - end - private - def repo_path - project.disk_path - end - - def wiki_path - project.wiki.disk_path - end - def trash_repositories! - unless remove_repository(repo_path) + unless remove_repository(project.repository) raise_error(s_('DeleteProject|Failed to remove project repository. Please try again or contact administrator.')) end - unless remove_repository(wiki_path) + unless remove_repository(project.wiki.repository) raise_error(s_('DeleteProject|Failed to remove wiki repository. Please try again or contact administrator.')) end end - def remove_repository(path) - # There is a possibility project does not have repository or wiki - return true unless repo_exists?(path) + def remove_repository(repository) + return true unless repository - new_path = removal_path(path) + result = Repositories::DestroyService.new(repository).execute - if mv_repository(path, new_path) - log_info(%Q{Repository "#{path}" moved to "#{new_path}" for project "#{project.full_path}"}) - - project.run_after_commit do - GitlabShellWorker.perform_in(REPO_REMOVAL_DELAY, :remove_repository, self.repository_storage, new_path) - end - else - false - end + result[:status] == :success end def schedule_stale_repos_removal - repo_paths = [removal_path(repo_path), removal_path(wiki_path)] + repos = [project.repository, project.wiki.repository] - # Ideally it should wait until the regular removal phase finishes, - # so let's delay it a bit further. - repo_paths.each do |path| - GitlabShellWorker.perform_in(REPO_REMOVAL_DELAY * 2, :remove_repository, project.repository_storage, path) - end - end + repos.each do |repository| + next unless repository - def rollback_repository(old_path, new_path) - # There is a possibility project does not have repository or wiki - return true unless repo_exists?(old_path) - - mv_repository(old_path, new_path) - end - - def repo_exists?(path) - gitlab_shell.repository_exists?(project.repository_storage, path + '.git') - end - - def mv_repository(from_path, to_path) - return true unless repo_exists?(from_path) - - gitlab_shell.mv_repository(project.repository_storage, from_path, to_path) + Repositories::ShellDestroyService.new(repository).execute(Repositories::ShellDestroyService::STALE_REMOVAL_DELAY) + end end def attempt_rollback(project, message) @@ -191,32 +140,9 @@ module Projects raise DestroyError.new(message) end - # Build a path for removing repositories - # We use `+` because its not allowed by GitLab so user can not create - # project with name cookies+119+deleted and capture someone stalled repository - # - # gitlab/cookies.git -> gitlab/cookies+119+deleted.git - # - def removal_path(path) - "#{path}+#{project.id}#{DELETED_FLAG}" - end - def flush_caches(project) - ignore_git_errors(repo_path) { project.repository.before_delete } - - ignore_git_errors(wiki_path) { Repository.new(wiki_path, project, disk_path: repo_path).before_delete } - Projects::ForksCountService.new(project).delete_cache end - - # If we get a Gitaly error, the repository may be corrupted. We can - # ignore these errors since we're going to trash the repositories - # anyway. - def ignore_git_errors(disk_path, &block) - yield - rescue Gitlab::Git::CommandError => e - Gitlab::GitLogger.warn(class: self.class.name, project_id: project.id, disk_path: disk_path, message: e.to_s) - end end end diff --git a/app/services/projects/overwrite_project_service.rb b/app/services/projects/overwrite_project_service.rb index c5e38f166da..64cc1c3aee3 100644 --- a/app/services/projects/overwrite_project_service.rb +++ b/app/services/projects/overwrite_project_service.rb @@ -55,7 +55,7 @@ module Projects end def attempt_restore_repositories(project) - ::Projects::DestroyService.new(project, @current_user).attempt_repositories_rollback + ::Projects::DestroyRollbackService.new(project, @current_user).execute end def add_source_project_to_fork_network(source_project) diff --git a/app/services/repositories/base_service.rb b/app/services/repositories/base_service.rb new file mode 100644 index 00000000000..6a39399c791 --- /dev/null +++ b/app/services/repositories/base_service.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class Repositories::BaseService < BaseService + include Gitlab::ShellAdapter + + DELETED_FLAG = '+deleted' + + attr_reader :repository + + delegate :project, :disk_path, :full_path, to: :repository + delegate :repository_storage, to: :project + + def initialize(repository) + @repository = repository + end + + def repo_exists?(path) + gitlab_shell.repository_exists?(repository_storage, path + '.git') + end + + def mv_repository(from_path, to_path) + return true unless repo_exists?(from_path) + + gitlab_shell.mv_repository(repository_storage, from_path, to_path) + end + + # Build a path for removing repositories + # We use `+` because its not allowed by GitLab so user can not create + # project with name cookies+119+deleted and capture someone stalled repository + # + # gitlab/cookies.git -> gitlab/cookies+119+deleted.git + # + def removal_path + "#{disk_path}+#{project.id}#{DELETED_FLAG}" + end + + # If we get a Gitaly error, the repository may be corrupted. We can + # ignore these errors since we're going to trash the repositories + # anyway. + def ignore_git_errors(&block) + yield + rescue Gitlab::Git::CommandError => e + Gitlab::GitLogger.warn(class: self.class.name, project_id: project.id, disk_path: disk_path, message: e.to_s) + end + + def move_error(path) + error = %Q{Repository "#{path}" could not be moved} + + log_error(error) + error(error) + end +end diff --git a/app/services/repositories/destroy_rollback_service.rb b/app/services/repositories/destroy_rollback_service.rb new file mode 100644 index 00000000000..5ef4e11bf55 --- /dev/null +++ b/app/services/repositories/destroy_rollback_service.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Repositories::DestroyRollbackService < Repositories::BaseService + def execute + # There is a possibility project does not have repository or wiki + return success unless repo_exists?(removal_path) + + # Flush the cache for both repositories. + ignore_git_errors { repository.before_delete } + + if mv_repository(removal_path, disk_path) + log_info(%Q{Repository "#{removal_path}" moved to "#{disk_path}" for repository "#{full_path}"}) + + success + else + move_error(removal_path) + end + end +end diff --git a/app/services/repositories/destroy_service.rb b/app/services/repositories/destroy_service.rb new file mode 100644 index 00000000000..374968f610e --- /dev/null +++ b/app/services/repositories/destroy_service.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class Repositories::DestroyService < Repositories::BaseService + def execute + return success unless repository + return success unless repo_exists?(disk_path) + + # Flush the cache for both repositories. This has to be done _before_ + # removing the physical repositories as some expiration code depends on + # Git data (e.g. a list of branch names). + ignore_git_errors { repository.before_delete } + + if mv_repository(disk_path, removal_path) + log_info(%Q{Repository "#{disk_path}" moved to "#{removal_path}" for repository "#{full_path}"}) + + current_repository = repository + project.run_after_commit do + Repositories::ShellDestroyService.new(current_repository).execute + end + + log_info("Project \"#{project.full_path}\" was removed") + + success + else + move_error(disk_path) + end + end +end diff --git a/app/services/repositories/shell_destroy_service.rb b/app/services/repositories/shell_destroy_service.rb new file mode 100644 index 00000000000..2f5af10e24c --- /dev/null +++ b/app/services/repositories/shell_destroy_service.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Repositories::ShellDestroyService < Repositories::BaseService + REPO_REMOVAL_DELAY = 5.minutes.to_i + STALE_REMOVAL_DELAY = REPO_REMOVAL_DELAY * 2 + + def execute(delay = REPO_REMOVAL_DELAY) + return success unless repository + + GitlabShellWorker.perform_in(delay, + :remove_repository, + repository_storage, + removal_path) + end +end |