Commit d7df806c authored by Gabriel Mazetto's avatar Gabriel Mazetto

Migrate BaseTransfer to use HTTP.rb instead of HTTParty

Specs now use stub_request instead of the multiple mocks it was using
before, so we can actually test the correct behavior
parent 22f29fa1
...@@ -43,6 +43,11 @@ module Gitlab ...@@ -43,6 +43,11 @@ module Gitlab
true true
end end
# @return [String] URL to download the resource from
def resource_url
Gitlab::Geo.primary_node.geo_transfers_url(file_type, file_id.to_s)
end
# Returns Result object with success boolean and number of bytes downloaded. # Returns Result object with success boolean and number of bytes downloaded.
def download_from_primary def download_from_primary
return skipped_result unless can_transfer? return skipped_result unless can_transfer?
...@@ -53,10 +58,9 @@ module Gitlab ...@@ -53,10 +58,9 @@ module Gitlab
return skipped_result return skipped_result
end end
url = Gitlab::Geo.primary_node.geo_transfers_url(file_type, file_id.to_s)
req_headers = TransferRequest.new(request_data).headers req_headers = TransferRequest.new(request_data).headers
download_file(url, req_headers) download_file(resource_url, req_headers)
end end
class Result class Result
...@@ -100,8 +104,7 @@ module Gitlab ...@@ -100,8 +104,7 @@ module Gitlab
true true
end end
# Use Gitlab::HTTP for now but switch to curb if performance becomes # Download file from informed URL using HTTP.rb
# an issue
# #
# @return [Result] Object with transfer status and information # @return [Result] Object with transfer status and information
def download_file(url, req_headers) def download_file(url, req_headers)
...@@ -111,37 +114,42 @@ module Gitlab ...@@ -111,37 +114,42 @@ module Gitlab
return failure_result unless temp_file return failure_result unless temp_file
begin begin
response = Gitlab::HTTP.get(url, allow_local_requests: true, headers: req_headers, stream_body: true) do |fragment| # Make the request
temp_file.write(fragment) response = ::HTTP.get(url, headers: req_headers)
end
temp_file.flush # Check for failures
unless response.status.success?
unless response.success? log_error("Unsuccessful download", filename: filename, status_code: response.status.code, reason: response.status.reason, url: url)
log_error("Unsuccessful download", filename: filename, response_code: response.code, response_msg: response.try(:msg), url: url)
return failure_result(primary_missing_file: primary_missing_file?(response, temp_file)) return failure_result(primary_missing_file: primary_missing_file?(response))
end end
if File.directory?(filename) # Stream to temporary file on disk
log_error("Destination file is a directory", filename: filename) response.body.each do |chunk|
temp_file.write(chunk)
return failure_result
end end
# Make sure file is written to the disk
# This is required to get correct file size.
temp_file.flush
file_size = File.stat(temp_file.path).size file_size = File.stat(temp_file.path).size
# Check for checksum mismatch
if checksum_mismatch?(temp_file.path) if checksum_mismatch?(temp_file.path)
log_error("Downloaded file checksum mismatch", expected_checksum: expected_checksum, actual_checksum: @actual_checksum, file_size_bytes: file_size) log_error("Downloaded file checksum mismatch", expected_checksum: expected_checksum, actual_checksum: @actual_checksum, file_size_bytes: file_size)
return failure_result(bytes_downloaded: file_size) return failure_result(bytes_downloaded: file_size)
end end
# Move transfered file to the target location
FileUtils.mv(temp_file.path, filename) FileUtils.mv(temp_file.path, filename)
log_info("Successful downloaded", filename: filename, file_size_bytes: file_size) log_info("Successful downloaded", filename: filename, file_size_bytes: file_size)
rescue StandardError, Gitlab::HTTP::Error => e rescue StandardError, ::HTTP::Error => e
log_error("Error downloading file", error: e, filename: filename, url: url) log_error("Error downloading file", error: e, filename: filename, url: url)
return failure_result
ensure ensure
temp_file.close temp_file.close
temp_file.unlink temp_file.unlink
...@@ -150,12 +158,11 @@ module Gitlab ...@@ -150,12 +158,11 @@ module Gitlab
Result.new(success: file_size > -1, bytes_downloaded: [file_size, 0].max) Result.new(success: file_size > -1, bytes_downloaded: [file_size, 0].max)
end end
def primary_missing_file?(response, temp_file) def primary_missing_file?(response)
body = File.read(temp_file.path) if File.exist?(temp_file.path) if response.status.not_found?
if response.code == 404 && body.present?
begin begin
json_response = JSON.parse(body) json_response = response.parse
return code_file_not_found?(json_response['geo_code']) return code_file_not_found?(json_response['geo_code'])
rescue JSON::ParserError rescue JSON::ParserError
end end
...@@ -180,7 +187,7 @@ module Gitlab ...@@ -180,7 +187,7 @@ module Gitlab
temp.binmode temp.binmode
temp temp
rescue StandardError => e rescue StandardError => e
log_error("Error creating temporary file", error: e) log_error("Error creating temporary file", error: e, filename: target_filename)
nil nil
end end
......
...@@ -46,15 +46,14 @@ describe Gitlab::Geo::Replication::FileTransfer do ...@@ -46,15 +46,14 @@ describe Gitlab::Geo::Replication::FileTransfer do
it 'returns a successful result' do it 'returns a successful result' do
content = upload.build_uploader.file.read content = upload.build_uploader.file.read
size = content.bytesize size = content.bytesize
stub_request(:get, subject.resource_url).to_return(status: 200, body: content)
expect(FileUtils).to receive(:mv).with(anything, upload.absolute_path).and_call_original
response = double(:response, success?: true)
expect(Gitlab::HTTP).to receive(:get).and_yield(content.to_s).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false) expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false)
stat = File.stat(upload.absolute_path) stat = File.stat(upload.absolute_path)
expect(stat.size).to eq(size) expect(stat.size).to eq(size)
expect(stat.mode & 0777).to eq(0666 - File.umask) expect(stat.mode & 0777).to eq(0666 - File.umask)
expect(File.binread(upload.absolute_path)).to eq(content) expect(File.binread(upload.absolute_path)).to eq(content)
...@@ -64,11 +63,10 @@ describe Gitlab::Geo::Replication::FileTransfer do ...@@ -64,11 +63,10 @@ describe Gitlab::Geo::Replication::FileTransfer do
context 'when the HTTP response is unsuccessful' do context 'when the HTTP response is unsuccessful' do
context 'when the HTTP response indicates a missing file on the primary' do context 'when the HTTP response indicates a missing file on the primary' do
it 'returns a failed result indicating primary_missing_file' do it 'returns a failed result indicating primary_missing_file' do
expect(FileUtils).not_to receive(:mv).with(anything, upload.absolute_path).and_call_original stub_request(:get, subject.resource_url)
response = double(:response, success?: false, code: 404, msg: "No such file") .to_return(status: 404,
headers: { content_type: 'application/json' },
expect(File).to receive(:read).and_return("{\"geo_code\":\"#{Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE}\"}") body: { geo_code: Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE }.to_json)
expect(Gitlab::HTTP).to receive(:get).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -78,10 +76,7 @@ describe Gitlab::Geo::Replication::FileTransfer do ...@@ -78,10 +76,7 @@ describe Gitlab::Geo::Replication::FileTransfer do
context 'when the HTTP response does not indicate a missing file on the primary' do context 'when the HTTP response does not indicate a missing file on the primary' do
it 'returns a failed result' do it 'returns a failed result' do
expect(FileUtils).not_to receive(:mv).with(anything, upload.absolute_path).and_call_original stub_request(:get, subject.resource_url).to_return(status: 404, body: 'Not found')
response = double(:response, success?: false, code: 404, msg: 'No such file')
expect(Gitlab::HTTP).to receive(:get).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -121,8 +116,8 @@ describe Gitlab::Geo::Replication::FileTransfer do ...@@ -121,8 +116,8 @@ describe Gitlab::Geo::Replication::FileTransfer do
context 'when the checksum of the downloaded file does not match' do context 'when the checksum of the downloaded file does not match' do
it 'returns a failed result' do it 'returns a failed result' do
bad_content = 'corrupted!!!' bad_content = 'corrupted!!!'
response = double(:response, success?: true) stub_request(:get, subject.resource_url)
expect(Gitlab::HTTP).to receive(:get).and_yield(bad_content).and_return(response) .to_return(status: 200, body: bad_content)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -134,8 +129,8 @@ describe Gitlab::Geo::Replication::FileTransfer do ...@@ -134,8 +129,8 @@ describe Gitlab::Geo::Replication::FileTransfer do
it 'returns a successful result' do it 'returns a successful result' do
upload.update_column(:checksum, nil) upload.update_column(:checksum, nil)
content = 'foo' content = 'foo'
response = double(:response, success?: true) stub_request(:get, subject.resource_url)
expect(Gitlab::HTTP).to receive(:get).and_yield(content).and_return(response) .to_return(status: 200, body: content)
result = subject.download_from_primary result = subject.download_from_primary
......
...@@ -54,14 +54,14 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do ...@@ -54,14 +54,14 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do
it 'returns a successful result' do it 'returns a successful result' do
content = job_artifact.file.read content = job_artifact.file.read
size = content.bytesize size = content.bytesize
expect(FileUtils).to receive(:mv).with(anything, job_artifact.file.path).and_call_original stub_request(:get, subject.resource_url).to_return(status: 200, body: content)
response = double(:response, success?: true)
expect(Gitlab::HTTP).to receive(:get).and_yield(content.to_s).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false) expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false)
stat = File.stat(job_artifact.file.path) stat = File.stat(job_artifact.file.path)
expect(stat.size).to eq(size) expect(stat.size).to eq(size)
expect(stat.mode & 0777).to eq(0666 - File.umask) expect(stat.mode & 0777).to eq(0666 - File.umask)
expect(File.binread(job_artifact.file.path)).to eq(content) expect(File.binread(job_artifact.file.path)).to eq(content)
...@@ -71,10 +71,10 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do ...@@ -71,10 +71,10 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do
context 'when the HTTP response is unsuccessful' do context 'when the HTTP response is unsuccessful' do
context 'when the HTTP response indicates a missing file on the primary' do context 'when the HTTP response indicates a missing file on the primary' do
it 'returns a failed result indicating primary_missing_file' do it 'returns a failed result indicating primary_missing_file' do
expect(FileUtils).not_to receive(:mv).with(anything, job_artifact.file.path).and_call_original stub_request(:get, subject.resource_url)
response = double(:response, success?: false, code: 404, msg: "No such file") .to_return(status: 404,
expect(File).to receive(:read).and_return("{\"geo_code\":\"#{Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE}\"}") headers: { content_type: 'application/json' },
expect(Gitlab::HTTP).to receive(:get).and_return(response) body: { geo_code: Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE }.to_json)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -84,9 +84,7 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do ...@@ -84,9 +84,7 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do
context 'when the HTTP response does not indicate a missing file on the primary' do context 'when the HTTP response does not indicate a missing file on the primary' do
it 'returns a failed result' do it 'returns a failed result' do
expect(FileUtils).not_to receive(:mv).with(anything, job_artifact.file.path).and_call_original stub_request(:get, subject.resource_url).to_return(status: 404, body: 'Not found')
response = double(:response, success?: false, code: 404, msg: 'No such file')
expect(Gitlab::HTTP).to receive(:get).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -124,8 +122,8 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do ...@@ -124,8 +122,8 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do
context 'when the checksum of the downloaded file does not match' do context 'when the checksum of the downloaded file does not match' do
it 'returns a failed result' do it 'returns a failed result' do
bad_content = 'corrupted!!!' bad_content = 'corrupted!!!'
response = double(:response, success?: true) stub_request(:get, subject.resource_url)
expect(Gitlab::HTTP).to receive(:get).and_yield(bad_content).and_return(response) .to_return(status: 200, body: bad_content)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -137,10 +135,11 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do ...@@ -137,10 +135,11 @@ describe Gitlab::Geo::Replication::JobArtifactTransfer, :geo do
it 'returns a successful result' do it 'returns a successful result' do
artifact = create(:ci_job_artifact, :archive) artifact = create(:ci_job_artifact, :archive)
content = 'foo' content = 'foo'
response = double(:response, success?: true)
expect(Gitlab::HTTP).to receive(:get).and_yield(content).and_return(response)
transfer = described_class.new(artifact) transfer = described_class.new(artifact)
stub_request(:get, transfer.resource_url)
.to_return(status: 200, body: content)
result = transfer.download_from_primary result = transfer.download_from_primary
expect_result(result, success: true, bytes_downloaded: content.bytesize, primary_missing_file: false) expect_result(result, success: true, bytes_downloaded: content.bytesize, primary_missing_file: false)
......
...@@ -32,14 +32,14 @@ describe Gitlab::Geo::Replication::LfsTransfer do ...@@ -32,14 +32,14 @@ describe Gitlab::Geo::Replication::LfsTransfer do
it 'returns a successful result' do it 'returns a successful result' do
content = lfs_object.file.read content = lfs_object.file.read
size = content.bytesize size = content.bytesize
expect(FileUtils).to receive(:mv).with(anything, lfs_object.file.path).and_call_original stub_request(:get, subject.resource_url).to_return(status: 200, body: content)
response = double(:response, success?: true)
expect(Gitlab::HTTP).to receive(:get).and_yield(content.to_s).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false) expect_result(result, success: true, bytes_downloaded: size, primary_missing_file: false)
stat = File.stat(lfs_object.file.path) stat = File.stat(lfs_object.file.path)
expect(stat.size).to eq(size) expect(stat.size).to eq(size)
expect(stat.mode & 0777).to eq(0666 - File.umask) expect(stat.mode & 0777).to eq(0666 - File.umask)
expect(File.binread(lfs_object.file.path)).to eq(content) expect(File.binread(lfs_object.file.path)).to eq(content)
...@@ -49,10 +49,10 @@ describe Gitlab::Geo::Replication::LfsTransfer do ...@@ -49,10 +49,10 @@ describe Gitlab::Geo::Replication::LfsTransfer do
context 'when the HTTP response is unsuccessful' do context 'when the HTTP response is unsuccessful' do
context 'when the HTTP response indicates a missing file on the primary' do context 'when the HTTP response indicates a missing file on the primary' do
it 'returns a failed result indicating primary_missing_file' do it 'returns a failed result indicating primary_missing_file' do
expect(FileUtils).not_to receive(:mv).with(anything, lfs_object.file.path).and_call_original stub_request(:get, subject.resource_url)
response = double(:response, success?: false, code: 404, msg: "No such file") .to_return(status: 404,
expect(File).to receive(:read).and_return("{\"geo_code\":\"#{Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE}\"}") headers: { content_type: 'application/json' },
expect(Gitlab::HTTP).to receive(:get).and_return(response) body: { geo_code: Gitlab::Geo::Replication::FILE_NOT_FOUND_GEO_CODE }.to_json)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -62,9 +62,7 @@ describe Gitlab::Geo::Replication::LfsTransfer do ...@@ -62,9 +62,7 @@ describe Gitlab::Geo::Replication::LfsTransfer do
context 'when the HTTP response does not indicate a missing file on the primary' do context 'when the HTTP response does not indicate a missing file on the primary' do
it 'returns a failed result' do it 'returns a failed result' do
expect(FileUtils).not_to receive(:mv).with(anything, lfs_object.file.path).and_call_original stub_request(:get, subject.resource_url).to_return(status: 404, body: 'Not found')
response = double(:response, success?: false, code: 404, msg: 'No such file')
expect(Gitlab::HTTP).to receive(:get).and_return(response)
result = subject.download_from_primary result = subject.download_from_primary
...@@ -102,8 +100,8 @@ describe Gitlab::Geo::Replication::LfsTransfer do ...@@ -102,8 +100,8 @@ describe Gitlab::Geo::Replication::LfsTransfer do
context 'when the checksum of the downloaded file does not match' do context 'when the checksum of the downloaded file does not match' do
it 'returns a failed result' do it 'returns a failed result' do
bad_content = 'corrupted!!!' bad_content = 'corrupted!!!'
response = double(:response, success?: true) stub_request(:get, subject.resource_url)
expect(Gitlab::HTTP).to receive(:get).and_yield(bad_content).and_return(response) .to_return(status: 200, body: bad_content)
result = subject.download_from_primary result = subject.download_from_primary
......
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