Commit b7e17708 authored by Nick Thomas's avatar Nick Thomas

Remove repository import code from Gitlab::Shell

The import_repository GRPC call is used when importing a project from
a remote source. Historically, this was functionality we'd ask a
subprocess in gitlab-shell to do. Now, Gitaly does it for us instead.

Refactor the code so we no longer have to indirect via Gitlab::Shell
to perform this call. It makes what is happening much clearer, and is
a precursor to removing all references to Gitlab::Shell.
parent 72d37d24
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
module Projects module Projects
class ImportService < BaseService class ImportService < BaseService
include Gitlab::ShellAdapter
Error = Class.new(StandardError) Error = Class.new(StandardError)
# Returns true if this importer is supposed to perform its work in the # Returns true if this importer is supposed to perform its work in the
...@@ -72,9 +70,9 @@ module Projects ...@@ -72,9 +70,9 @@ module Projects
project.ensure_repository project.ensure_repository
project.repository.fetch_as_mirror(project.import_url, refmap: refmap) project.repository.fetch_as_mirror(project.import_url, refmap: refmap)
else else
gitlab_shell.import_project_repository(project) project.repository.import_repository(project.import_url)
end end
rescue Gitlab::Shell::Error => e rescue ::Gitlab::Git::CommandError => e
# Expire cache to prevent scenarios such as: # Expire cache to prevent scenarios such as:
# 1. First import failed, but the repo was imported successfully, so +exists?+ returns true # 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 # 2. Retried import, repo is broken or not imported but +exists?+ still returns true
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
module Gitlab module Gitlab
module BitbucketImport module BitbucketImport
class Importer class Importer
include Gitlab::ShellAdapter
LABELS = [{ title: 'bug', color: '#FF0000' }, LABELS = [{ title: 'bug', color: '#FF0000' },
{ title: 'enhancement', color: '#428BCA' }, { title: 'enhancement', color: '#428BCA' },
{ title: 'proposal', color: '#69D100' }, { title: 'proposal', color: '#69D100' },
...@@ -80,7 +78,7 @@ module Gitlab ...@@ -80,7 +78,7 @@ module Gitlab
wiki = WikiFormatter.new(project) wiki = WikiFormatter.new(project)
gitlab_shell.import_wiki_repository(project, wiki) project.wiki.repository.import_repository(wiki.import_url)
rescue StandardError => e rescue StandardError => e
errors << { type: :wiki, errors: e.message } errors << { type: :wiki, errors: e.message }
end end
......
...@@ -793,6 +793,14 @@ module Gitlab ...@@ -793,6 +793,14 @@ module Gitlab
end end
end end
def import_repository(url)
raise ArgumentError, "don't use disk paths with import_repository: #{url.inspect}" if url.start_with?('.', '/')
wrapped_gitaly_errors do
gitaly_repository_client.import_repository(url)
end
end
def blob_at(sha, path) def blob_at(sha, path)
Gitlab::Git::Blob.find(self, sha, path) unless Gitlab::Git.blank_ref?(sha) Gitlab::Git::Blob.find(self, sha, path) unless Gitlab::Git.blank_ref?(sha)
end end
......
...@@ -4,7 +4,6 @@ module Gitlab ...@@ -4,7 +4,6 @@ module Gitlab
module GithubImport module GithubImport
module Importer module Importer
class RepositoryImporter class RepositoryImporter
include Gitlab::ShellAdapter
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :project, :client, :wiki_formatter attr_reader :project, :client, :wiki_formatter
...@@ -65,10 +64,10 @@ module Gitlab ...@@ -65,10 +64,10 @@ module Gitlab
end end
def import_wiki_repository def import_wiki_repository
gitlab_shell.import_wiki_repository(project, wiki_formatter) project.wiki.repository.import_repository(wiki_formatter.import_url)
true true
rescue Gitlab::Shell::Error => e rescue ::Gitlab::Git::CommandError => e
if e.message !~ /repository not exported/ if e.message !~ /repository not exported/
project.create_wiki project.create_wiki
fail_import("Failed to import the wiki: #{e.message}") fail_import("Failed to import the wiki: #{e.message}")
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
module Gitlab module Gitlab
module LegacyGithubImport module LegacyGithubImport
class Importer class Importer
include Gitlab::ShellAdapter
def self.refmap def self.refmap
Gitlab::GithubImport.refmap Gitlab::GithubImport.refmap
end end
...@@ -264,11 +262,11 @@ module Gitlab ...@@ -264,11 +262,11 @@ module Gitlab
end end
def import_wiki def import_wiki
unless project.wiki.repository_exists? return if project.wiki.repository_exists?
wiki = WikiFormatter.new(project)
gitlab_shell.import_wiki_repository(project, wiki) wiki = WikiFormatter.new(project)
end project.wiki.repository.import_repository(wiki.import_url)
rescue Gitlab::Shell::Error => e rescue ::Gitlab::Git::CommandError => e
# GitHub error message when the wiki repo has not been created, # GitHub error message when the wiki repo has not been created,
# this means that repo has wiki enabled, but have no pages. So, # this means that repo has wiki enabled, but have no pages. So,
# we can skip the import. # we can skip the import.
......
...@@ -77,47 +77,6 @@ module Gitlab ...@@ -77,47 +77,6 @@ module Gitlab
end end
end end
# Import wiki repository from external service
#
# @param [Project] project
# @param [Gitlab::LegacyGithubImport::WikiFormatter, Gitlab::BitbucketImport::WikiFormatter] wiki_formatter
# @return [Boolean] whether repository could be imported
def import_wiki_repository(project, wiki_formatter)
import_repository(project.repository_storage, wiki_formatter.disk_path, wiki_formatter.import_url, project.wiki.full_path)
end
# Import project repository from external service
#
# @param [Project] project
# @return [Boolean] whether repository could be imported
def import_project_repository(project)
import_repository(project.repository_storage, project.disk_path, project.import_url, project.full_path)
end
# Import repository
#
# @example Import a repository
# import_repository("nfs-file06", "gitlab/gitlab-ci", "https://gitlab.com/gitlab-org/gitlab-test.git", "gitlab/gitlab-ci")
#
# @param [String] storage project's storage name
# @param [String] disk_path project path on disk
# @param [String] url from external resource to import from
# @param [String] gl_project_path project name
# @return [Boolean] whether repository could be imported
def import_repository(storage, disk_path, url, gl_project_path)
if url.start_with?('.', '/')
raise Error.new("don't use disk paths with import_repository: #{url.inspect}")
end
relative_path = "#{disk_path}.git"
cmd = GitalyGitlabProjects.new(storage, relative_path, gl_project_path)
success = cmd.import_project(url, git_timeout)
raise Error, cmd.output unless success
success
end
# Move or rename a repository # Move or rename a repository
# #
# @example Move/rename a repository # @example Move/rename a repository
...@@ -264,16 +223,6 @@ module Gitlab ...@@ -264,16 +223,6 @@ module Gitlab
@gl_project_path = gl_project_path @gl_project_path = gl_project_path
end end
def import_project(source, _timeout)
raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil, gl_project_path)
Gitlab::GitalyClient::RepositoryService.new(raw_repository).import_repository(source)
true
rescue GRPC::BadStatus => e
@output = e.message
false
end
def fork_repository(new_shard_name, new_repository_relative_path, new_project_name) def fork_repository(new_shard_name, new_repository_relative_path, new_project_name)
target_repository = Gitlab::Git::Repository.new(new_shard_name, new_repository_relative_path, nil, new_project_name) target_repository = Gitlab::Git::Repository.new(new_shard_name, new_repository_relative_path, nil, new_project_name)
raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil, gl_project_path) raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil, gl_project_path)
......
...@@ -80,8 +80,7 @@ describe Gitlab::BitbucketImport::Importer do ...@@ -80,8 +80,7 @@ describe Gitlab::BitbucketImport::Importer do
end end
let(:importer) { described_class.new(project) } let(:importer) { described_class.new(project) }
let(:gitlab_shell) { double } let(:sample) { RepoHelpers.sample_compare }
let(:issues_statuses_sample_data) do let(:issues_statuses_sample_data) do
{ {
count: sample_issues_statuses.count, count: sample_issues_statuses.count,
...@@ -89,12 +88,6 @@ describe Gitlab::BitbucketImport::Importer do ...@@ -89,12 +88,6 @@ describe Gitlab::BitbucketImport::Importer do
} }
end end
let(:sample) { RepoHelpers.sample_compare }
before do
allow(importer).to receive(:gitlab_shell) { gitlab_shell }
end
subject { described_class.new(project) } subject { described_class.new(project) }
describe '#import_pull_requests' do describe '#import_pull_requests' do
...@@ -316,7 +309,7 @@ describe Gitlab::BitbucketImport::Importer do ...@@ -316,7 +309,7 @@ describe Gitlab::BitbucketImport::Importer do
describe 'wiki import' do describe 'wiki import' do
it 'is skipped when the wiki exists' do it 'is skipped when the wiki exists' do
expect(project.wiki).to receive(:repository_exists?) { true } expect(project.wiki).to receive(:repository_exists?) { true }
expect(importer.gitlab_shell).not_to receive(:import_wiki_repository) expect(project.wiki.repository).not_to receive(:import_repository)
importer.execute importer.execute
...@@ -325,7 +318,7 @@ describe Gitlab::BitbucketImport::Importer do ...@@ -325,7 +318,7 @@ describe Gitlab::BitbucketImport::Importer do
it 'imports to the project disk_path' do it 'imports to the project disk_path' do
expect(project.wiki).to receive(:repository_exists?) { false } expect(project.wiki).to receive(:repository_exists?) { false }
expect(importer.gitlab_shell).to receive(:import_wiki_repository) expect(project.wiki.repository).to receive(:import_repository)
importer.execute importer.execute
......
...@@ -2138,6 +2138,33 @@ describe Gitlab::Git::Repository, :seed_helper do ...@@ -2138,6 +2138,33 @@ describe Gitlab::Git::Repository, :seed_helper do
end end
end end
describe '#import_repository' do
let_it_be(:project) { create(:project) }
let(:repository) { project.repository }
let(:url) { 'http://invalid.invalid' }
it 'raises an error if a relative path is provided' do
expect { repository.import_repository('/foo') }.to raise_error(ArgumentError, /disk path/)
end
it 'raises an error if an absolute path is provided' do
expect { repository.import_repository('./foo') }.to raise_error(ArgumentError, /disk path/)
end
it 'delegates to Gitaly' do
expect_next_instance_of(Gitlab::GitalyClient::RepositoryService) do |svc|
expect(svc).to receive(:import_repository).with(url).and_return(nil)
end
repository.import_repository(url)
end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :import_repository do
subject { repository.import_repository('http://invalid.invalid') }
end
end
describe '#replicate' do describe '#replicate' do
let(:new_repository) do let(:new_repository) do
Gitlab::Git::Repository.new('test_second_storage', TEST_REPO_PATH, '', 'group/project') Gitlab::Git::Repository.new('test_second_storage', TEST_REPO_PATH, '', 'group/project')
......
...@@ -11,10 +11,15 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -11,10 +11,15 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
double( double(
:wiki, :wiki,
disk_path: 'foo.wiki', disk_path: 'foo.wiki',
full_path: 'group/foo.wiki' full_path: 'group/foo.wiki',
repository: wiki_repository
) )
end end
let(:wiki_repository) do
double(:wiki_repository)
end
let(:project) do let(:project) do
double( double(
:project, :project,
...@@ -221,17 +226,19 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -221,17 +226,19 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_wiki_repository' do describe '#import_wiki_repository' do
it 'imports the wiki repository' do it 'imports the wiki repository' do
expect(importer.gitlab_shell) expect(wiki_repository)
.to receive(:import_repository) .to receive(:import_repository)
.with('foo', 'foo.wiki', 'foo.wiki.git', 'group/foo.wiki') .with(importer.wiki_url)
.and_return(true)
expect(importer.import_wiki_repository).to eq(true) expect(importer.import_wiki_repository).to eq(true)
end end
it 'marks the import as failed and creates an empty repo if an error was raised' do it 'marks the import as failed and creates an empty repo if an error was raised' do
expect(importer.gitlab_shell) expect(wiki_repository)
.to receive(:import_repository) .to receive(:import_repository)
.and_raise(Gitlab::Shell::Error) .with(importer.wiki_url)
.and_raise(Gitlab::Git::CommandError)
expect(importer) expect(importer)
.to receive(:fail_import) .to receive(:fail_import)
......
...@@ -45,7 +45,7 @@ describe Gitlab::LegacyGithubImport::Importer do ...@@ -45,7 +45,7 @@ describe Gitlab::LegacyGithubImport::Importer do
allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new)
allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound)
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) allow(project.wiki.repository).to receive(:import_repository).and_raise(Gitlab::Git::CommandError)
allow_any_instance_of(Octokit::Client).to receive(:user).and_return(octocat) allow_any_instance_of(Octokit::Client).to receive(:user).and_return(octocat)
allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2])
...@@ -169,7 +169,7 @@ describe Gitlab::LegacyGithubImport::Importer do ...@@ -169,7 +169,7 @@ describe Gitlab::LegacyGithubImport::Importer do
errors: [ errors: [
{ type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, { type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" },
{ type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, { type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" },
{ type: :wiki, errors: "Gitlab::Shell::Error" } { type: :wiki, errors: "Gitlab::Git::CommandError" }
] ]
} }
......
...@@ -117,30 +117,6 @@ describe Gitlab::Shell do ...@@ -117,30 +117,6 @@ describe Gitlab::Shell do
is_expected.to be_falsy is_expected.to be_falsy
end end
end end
describe '#import_repository' do
let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-foss.git' }
context 'with gitaly' do
it 'returns true when the command succeeds' do
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository).with(import_url)
result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url, project.full_path)
expect(result).to be_truthy
end
it 'raises an exception when the command fails' do
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository)
.with(import_url) { raise GRPC::BadStatus, 'bla' }
expect_any_instance_of(Gitlab::Shell::GitalyGitlabProjects).to receive(:output) { 'error'}
expect do
gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url, project.full_path)
end.to raise_error(Gitlab::Shell::Error, "error")
end
end
end
end end
describe 'namespace actions' do describe 'namespace actions' do
......
...@@ -122,7 +122,7 @@ describe Projects::ImportService do ...@@ -122,7 +122,7 @@ describe Projects::ImportService do
end end
it 'succeeds if repository import is successful' do it 'succeeds if repository import is successful' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true) expect(project.repository).to receive(:import_repository).and_return(true)
expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true) expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true)
expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :success) expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :success)
...@@ -132,7 +132,9 @@ describe Projects::ImportService do ...@@ -132,7 +132,9 @@ describe Projects::ImportService do
end end
it 'fails if repository import fails' do it 'fails if repository import fails' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error.new('Failed to import the repository /a/b/c')) expect(project.repository)
.to receive(:import_repository)
.and_raise(Gitlab::Git::CommandError, 'Failed to import the repository /a/b/c')
result = subject.execute result = subject.execute
...@@ -144,7 +146,7 @@ describe Projects::ImportService do ...@@ -144,7 +146,7 @@ describe Projects::ImportService do
it 'logs the error' do it 'logs the error' do
error_message = 'error message' error_message = 'error message'
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true) expect(project.repository).to receive(:import_repository).and_return(true)
expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true) expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true)
expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: error_message) expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: error_message)
expect(Gitlab::AppLogger).to receive(:error).with("The Lfs import process failed. #{error_message}") expect(Gitlab::AppLogger).to receive(:error).with("The Lfs import process failed. #{error_message}")
...@@ -155,7 +157,7 @@ describe Projects::ImportService do ...@@ -155,7 +157,7 @@ describe Projects::ImportService do
context 'when repository import scheduled' do context 'when repository import scheduled' do
before do before do
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true) expect(project.repository).to receive(:import_repository).and_return(true)
allow(subject).to receive(:import_data) allow(subject).to receive(:import_data)
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