1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
# frozen_string_literal: true
module Projects
class ImportService < BaseService
Error = Class.new(StandardError)
PermissionError = Class.new(StandardError)
# Returns true if this importer is supposed to perform its work in the
# background.
#
# This method will only return `true` if async importing is explicitly
# supported by an importer class (`Gitlab::GithubImport::ParallelImporter`
# for example).
def async?
has_importer? && !!importer_class.try(:async?)
end
def execute
track_start_import
add_repository_to_project
validate_repository_size!
download_lfs_objects
import_data
after_execute_hook
success
rescue Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError, StandardError => e
Gitlab::Import::ImportFailureService.track(
project_id: project.id,
error_source: self.class.name,
exception: e,
metrics: true
)
message = Projects::ImportErrorFilter.filter_message(e.message)
error(
s_(
"ImportProjects|Error importing repository %{project_safe_import_url} into %{project_full_path} - %{message}"
) % { project_safe_import_url: project.safe_import_url, project_full_path: project.full_path, message: message }
)
end
protected
def extra_attributes_for_measurement
{
current_user: current_user&.name,
project_full_path: project&.full_path,
import_type: project&.import_type,
file_path: project&.import_source
}
end
private
attr_reader :resolved_address
def validate_repository_size!
# Defined in EE::Projects::ImportService
end
def after_execute_hook
# Defined in EE::Projects::ImportService
end
def track_start_import
has_importer? && importer_class.try(:track_start_import, project)
end
def add_repository_to_project
if project.external_import? && !unknown_url?
begin
@resolved_address = get_resolved_address
rescue Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError => e
raise e, s_("ImportProjects|Blocked import URL: %{message}") % { message: e.message }
end
end
# We should skip the repository for a GitHub import or GitLab project import,
# because these importers fetch the project repositories for us.
return if importer_imports_repository?
if unknown_url?
# In this case, we only want to import issues, not a repository.
create_repository
elsif !project.repository_exists?
import_repository
end
end
def create_repository
unless project.create_repository
raise Error, s_('ImportProjects|The repository could not be created.')
end
end
def import_repository
refmap = importer_class.try(:refmap) if has_importer?
if refmap
project.ensure_repository
project.repository.fetch_as_mirror(project.import_url, refmap: refmap, resolved_address: resolved_address)
else
project.repository.import_repository(project.import_url, resolved_address: resolved_address)
end
rescue ::Gitlab::Git::CommandError => e
# Expire cache to prevent scenarios such as:
# 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true
project.repository.expire_content_cache if project.repository_exists?
raise Error, e.message
end
def download_lfs_objects
# In this case, we only want to import issues
return if unknown_url?
# If it has its own repository importer, it has to implements its own lfs import download
return if importer_imports_repository?
return unless project.lfs_enabled?
result = Projects::LfsPointers::LfsImportService.new(project).execute
if result[:status] == :error
# To avoid aborting the importing process, we silently fail
# if any exception raises.
Gitlab::AppLogger.error("The Lfs import process failed. #{result[:message]}")
end
end
def import_data
return unless has_importer?
project.repository.expire_content_cache unless project.gitlab_project_import?
unless importer.execute
raise Error, s_('ImportProjects|The remote data could not be imported.')
end
end
def importer_class
@importer_class ||= Gitlab::ImportSources.importer(project.import_type)
end
def has_importer?
Gitlab::ImportSources.importer_names.include?(project.import_type)
end
def importer
importer_class.new(project)
end
def unknown_url?
project.import_url == Project::UNKNOWN_IMPORT_URL
end
def importer_imports_repository?
has_importer? && importer_class.try(:imports_repository?)
end
def get_resolved_address
Gitlab::HTTP_V2::UrlBlocker
.validate!(
project.import_url,
schemes: Project::VALID_IMPORT_PROTOCOLS,
ports: Project::VALID_IMPORT_PORTS,
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
dns_rebind_protection: dns_rebind_protection?,
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?,
outbound_local_requests_allowlist: Gitlab::CurrentSettings.outbound_local_requests_whitelist) # rubocop:disable Naming/InclusiveLanguage -- existing setting
.then do |(import_url, resolved_host)|
next '' if resolved_host.nil? || !import_url.scheme.in?(%w[http https])
import_url.hostname.to_s
end
end
def allow_local_requests?
Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
end
def dns_rebind_protection?
return false if Gitlab.http_proxy_env?
Gitlab::CurrentSettings.dns_rebinding_protection_enabled?
end
end
end
Projects::ImportService.prepend_mod_with('Projects::ImportService')
|