Commit 660dcd5b authored by Stan Hu's avatar Stan Hu

Fix Bitbucket Server importer error handling

The importer would display a 500 error page if you attempted to import
using a non-existent DNS entry. This commit rescues known network issues
and consolidates them into
BitbucketServer::Connection::ConnectionError`.  The previous error
handling in the paginator doesn't work because it returns a lazy
collection.

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/56154
parent 1161c99e
...@@ -40,7 +40,7 @@ class Import::BitbucketServerController < Import::BaseController ...@@ -40,7 +40,7 @@ class Import::BitbucketServerController < Import::BaseController
else else
render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity
end end
rescue BitbucketServer::Client::ServerError => e rescue BitbucketServer::Connection::ConnectionError => e
render json: { errors: "Unable to connect to server: #{e}" }, status: :unprocessable_entity render json: { errors: "Unable to connect to server: #{e}" }, status: :unprocessable_entity
end end
...@@ -62,7 +62,7 @@ class Import::BitbucketServerController < Import::BaseController ...@@ -62,7 +62,7 @@ class Import::BitbucketServerController < Import::BaseController
already_added_projects_names = @already_added_projects.pluck(:import_source) already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) } @repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
rescue BitbucketServer::Connection::ConnectionError, BitbucketServer::Client::ServerError => e rescue BitbucketServer::Connection::ConnectionError => e
flash[:alert] = "Unable to connect to server: #{e}" flash[:alert] = "Unable to connect to server: #{e}"
clear_session_data clear_session_data
redirect_to new_import_bitbucket_server_path redirect_to new_import_bitbucket_server_path
......
---
title: Fix Bitbucket Server importer error handling
merge_request: 24343
author:
type: fixed
...@@ -4,18 +4,6 @@ module BitbucketServer ...@@ -4,18 +4,6 @@ module BitbucketServer
class Client class Client
attr_reader :connection attr_reader :connection
ServerError = Class.new(StandardError)
SERVER_ERRORS = [SocketError,
OpenSSL::SSL::SSLError,
Errno::ECONNRESET,
Errno::ECONNREFUSED,
Errno::EHOSTUNREACH,
Net::OpenTimeout,
Net::ReadTimeout,
Gitlab::HTTP::BlockedUrlError,
BitbucketServer::Connection::ConnectionError].freeze
def initialize(options = {}) def initialize(options = {})
@connection = Connection.new(options) @connection = Connection.new(options)
end end
...@@ -64,8 +52,6 @@ module BitbucketServer ...@@ -64,8 +52,6 @@ module BitbucketServer
def get_collection(path, type, page_offset: 0, limit: nil) def get_collection(path, type, page_offset: 0, limit: nil)
paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type, page_offset: page_offset, limit: limit) paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type, page_offset: page_offset, limit: limit)
BitbucketServer::Collection.new(paginator) BitbucketServer::Collection.new(paginator)
rescue *SERVER_ERRORS => e
raise ServerError, e
end end
end end
end end
...@@ -7,6 +7,17 @@ module BitbucketServer ...@@ -7,6 +7,17 @@ module BitbucketServer
DEFAULT_API_VERSION = '1.0' DEFAULT_API_VERSION = '1.0'
SEPARATOR = '/' SEPARATOR = '/'
NETWORK_ERRORS = [
SocketError,
OpenSSL::SSL::SSLError,
Errno::ECONNRESET,
Errno::ECONNREFUSED,
Errno::EHOSTUNREACH,
Net::OpenTimeout,
Net::ReadTimeout,
Gitlab::HTTP::BlockedUrlError
].freeze
attr_reader :api_version, :base_uri, :username, :token attr_reader :api_version, :base_uri, :username, :token
ConnectionError = Class.new(StandardError) ConnectionError = Class.new(StandardError)
...@@ -27,6 +38,8 @@ module BitbucketServer ...@@ -27,6 +38,8 @@ module BitbucketServer
check_errors!(response) check_errors!(response)
response.parsed_response response.parsed_response
rescue *NETWORK_ERRORS => e
raise ConnectionError, e
end end
def post(path, body) def post(path, body)
...@@ -38,6 +51,8 @@ module BitbucketServer ...@@ -38,6 +51,8 @@ module BitbucketServer
check_errors!(response) check_errors!(response)
response.parsed_response response.parsed_response
rescue *NETWORK_ERRORS => e
raise ConnectionError, e
end end
# We need to support two different APIs for deletion: # We need to support two different APIs for deletion:
...@@ -55,6 +70,8 @@ module BitbucketServer ...@@ -55,6 +70,8 @@ module BitbucketServer
check_errors!(response) check_errors!(response)
response.parsed_response response.parsed_response
rescue *NETWORK_ERRORS => e
raise ConnectionError, e
end end
private private
......
...@@ -78,7 +78,7 @@ describe Import::BitbucketServerController do ...@@ -78,7 +78,7 @@ describe Import::BitbucketServerController do
end end
it "returns an error when the server can't be contacted" do it "returns an error when the server can't be contacted" do
expect(client).to receive(:repo).with(project_key, repo_slug).and_raise(BitbucketServer::Client::ServerError) expect(client).to receive(:repo).with(project_key, repo_slug).and_raise(::BitbucketServer::Connection::ConnectionError)
post :create, params: { project: project_key, repository: repo_slug }, format: :json post :create, params: { project: project_key, repository: repo_slug }, format: :json
......
...@@ -17,12 +17,6 @@ describe BitbucketServer::Client do ...@@ -17,12 +17,6 @@ describe BitbucketServer::Client do
subject.pull_requests(project, repo_slug) subject.pull_requests(project, repo_slug)
end end
it 'throws an exception when connection fails' do
allow(BitbucketServer::Collection).to receive(:new).and_raise(OpenSSL::SSL::SSLError)
expect { subject.pull_requests(project, repo_slug) }.to raise_error(described_class::ServerError)
end
end end
describe '#activities' do describe '#activities' do
......
...@@ -26,6 +26,12 @@ describe BitbucketServer::Connection do ...@@ -26,6 +26,12 @@ describe BitbucketServer::Connection do
expect { subject.get(url) }.to raise_error(described_class::ConnectionError) expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
end end
it 'throws an exception upon a network error' do
WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_raise(OpenSSL::SSL::SSLError)
expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
end
end end
describe '#post' do describe '#post' do
...@@ -42,6 +48,12 @@ describe BitbucketServer::Connection do ...@@ -42,6 +48,12 @@ describe BitbucketServer::Connection do
expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError) expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
end end
it 'throws an exception upon a network error' do
WebMock.stub_request(:post, url).with(headers: { 'Accept' => 'application/json' }).to_raise(OpenSSL::SSL::SSLError)
expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
end
end end
describe '#delete' do describe '#delete' do
...@@ -63,6 +75,12 @@ describe BitbucketServer::Connection do ...@@ -63,6 +75,12 @@ describe BitbucketServer::Connection do
expect { subject.delete(:branches, branch_path, payload) }.to raise_error(described_class::ConnectionError) expect { subject.delete(:branches, branch_path, payload) }.to raise_error(described_class::ConnectionError)
end end
it 'throws an exception upon a network error' do
WebMock.stub_request(:delete, branch_url).with(headers: headers).to_raise(OpenSSL::SSL::SSLError)
expect { subject.delete(:branches, branch_path, payload) }.to raise_error(described_class::ConnectionError)
end
end end
end 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