45f94ea78c
There are two issues with truncating the project team early: 1. `Projects::UnlinkForkService` may not close merge requests properly since permissions may be revoked early. 2. If an error is encountered during flushing of caches, then the user will lose all privileges, possibly causing an issue on deletion on retry.
97 lines
3.1 KiB
Ruby
97 lines
3.1 KiB
Ruby
module Projects
|
|
class DestroyService < BaseService
|
|
include Gitlab::ShellAdapter
|
|
|
|
class DestroyError < StandardError; end
|
|
|
|
DELETED_FLAG = '+deleted'
|
|
|
|
def async_execute
|
|
project.transaction do
|
|
project.update_attribute(:pending_delete, true)
|
|
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
|
|
Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
|
|
end
|
|
end
|
|
|
|
def execute
|
|
return false unless can?(current_user, :remove_project, project)
|
|
|
|
repo_path = project.path_with_namespace
|
|
wiki_path = repo_path + '.wiki'
|
|
|
|
# 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).
|
|
flush_caches(project, wiki_path)
|
|
|
|
Projects::UnlinkForkService.new(project, current_user).execute
|
|
|
|
Project.transaction do
|
|
project.team.truncate
|
|
project.destroy!
|
|
|
|
unless remove_registry_tags
|
|
raise_error('Failed to remove project container registry. Please try again or contact administrator')
|
|
end
|
|
|
|
unless remove_repository(repo_path)
|
|
raise_error('Failed to remove project repository. Please try again or contact administrator')
|
|
end
|
|
|
|
unless remove_repository(wiki_path)
|
|
raise_error('Failed to remove wiki repository. Please try again or contact administrator')
|
|
end
|
|
end
|
|
|
|
log_info("Project \"#{project.path_with_namespace}\" was removed")
|
|
system_hook_service.execute_hooks_for(project, :destroy)
|
|
true
|
|
end
|
|
|
|
private
|
|
|
|
def remove_repository(path)
|
|
# Skip repository removal. We use this flag when remove user or group
|
|
return true if params[:skip_repo] == true
|
|
|
|
# There is a possibility project does not have repository or wiki
|
|
return true unless gitlab_shell.exists?(project.repository_storage_path, path + '.git')
|
|
|
|
new_path = removal_path(path)
|
|
|
|
if gitlab_shell.mv_repository(project.repository_storage_path, path, new_path)
|
|
log_info("Repository \"#{path}\" moved to \"#{new_path}\"")
|
|
GitlabShellWorker.perform_in(5.minutes, :remove_repository, project.repository_storage_path, new_path)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def remove_registry_tags
|
|
return true unless Gitlab.config.registry.enabled
|
|
|
|
project.container_registry_repository.delete_tags
|
|
end
|
|
|
|
def raise_error(message)
|
|
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, wiki_path)
|
|
project.repository.before_delete
|
|
|
|
Repository.new(wiki_path, project).before_delete
|
|
end
|
|
end
|
|
end
|