Does not sync uploads on Object Storage when is not enabled

Add some checks to skip file transfers on a Geo secondary
node when file is stored on Object Storage but syncing
object storage is disabled or the object storage is
not configure for the data type on the secondary node.
parent b2f063f8
...@@ -48,16 +48,16 @@ module Gitlab ...@@ -48,16 +48,16 @@ module Gitlab
def check_preconditions def check_preconditions
unless resource.present? unless resource.present?
return fail_before_transfer(reason: "Skipping transfer as the #{object_type.to_s.humanize(capitalize: false)} (ID = #{object_db_id}) could not be found") return skip_transfer_error(reason: "Skipping transfer as the #{object_type.to_s.humanize(capitalize: false)} (ID = #{object_db_id}) could not be found")
end end
unless local_store? unless local_store?
unless sync_object_storage_enabled? unless sync_object_storage_enabled?
return fail_before_transfer(reason: "Skipping transfer as this secondary node is not allowed to replicate content on Object Storage") return skip_transfer_error(reason: "Skipping transfer as this secondary node is not allowed to replicate content on Object Storage")
end end
unless object_store_enabled? unless object_store_enabled?
return fail_before_transfer(reason: "Skipping transfer as this secondary node is not configured to store #{object_type.to_s.humanize(capitalize: false)} on Object Storage") return skip_transfer_error(reason: "Skipping transfer as this secondary node is not configured to store #{object_type.to_s.humanize(capitalize: false)} on Object Storage")
end end
end end
...@@ -84,11 +84,11 @@ module Gitlab ...@@ -84,11 +84,11 @@ module Gitlab
Gitlab::Geo.current_node.sync_object_storage Gitlab::Geo.current_node.sync_object_storage
end end
def fail_before_transfer(reason: nil) def skip_transfer_error(reason: nil)
Result.new(success: false, bytes_downloaded: 0, reason: reason, failed_before_transfer: true) Result.new(success: false, bytes_downloaded: 0, reason: reason, failed_before_transfer: true)
end end
def missing_on_primary def missing_on_primary_error
Result.new(success: true, bytes_downloaded: 0, primary_missing_file: true) Result.new(success: true, bytes_downloaded: 0, primary_missing_file: true)
end end
end end
......
...@@ -9,30 +9,28 @@ module Gitlab ...@@ -9,30 +9,28 @@ module Gitlab
# * Returning a detailed Result # * Returning a detailed Result
# #
class FileDownloader < BaseDownloader class FileDownloader < BaseDownloader
# Executes the actual file download private
#
# Subclasses should return the number of bytes downloaded,
# or nil or -1 if a failure occurred.
def execute
upload = find_resource
return fail_before_transfer unless upload.present?
return missing_on_primary if upload.model.nil?
transfer = ::Gitlab::Geo::Replication::FileTransfer.new(object_type.to_sym, upload) def check_preconditions
return missing_on_primary_error if resource && resource.model.nil?
result = if upload.local? super
transfer.download_from_primary end
else
transfer.stream_from_primary_to_object_storage
end
Result.from_transfer_result(result) def local_store?
resource.local?
end end
private def resource
strong_memoize(:resource) { ::Upload.find_by_id(object_db_id) }
end
def transfer
strong_memoize(:transfer) { ::Gitlab::Geo::Replication::FileTransfer.new(object_type.to_sym, resource) }
end
def find_resource def object_store_enabled?
Upload.find_by_id(object_db_id) ::FileUploader.object_store_enabled?
end end
end end
end end
......
...@@ -5,37 +5,116 @@ require 'spec_helper' ...@@ -5,37 +5,116 @@ require 'spec_helper'
RSpec.describe Gitlab::Geo::Replication::FileDownloader, :geo do RSpec.describe Gitlab::Geo::Replication::FileDownloader, :geo do
include EE::GeoHelpers include EE::GeoHelpers
let_it_be(:primary_node) { create(:geo_node, :primary) } describe '#execute' do
let_it_be(:primary_node) { create(:geo_node, :primary) }
let_it_be(:secondary, reload: true) { create(:geo_node) }
subject { downloader.execute } before do
stub_current_geo_node(secondary)
end
let(:upload) { create(:upload, :issuable_upload, :with_file) } context 'with upload' do
let(:downloader) { described_class.new(:file, upload.id) } context 'on local storage' do
let(:upload) { create(:upload, :with_file) }
context 'when in primary geo node' do subject(:downloader) { described_class.new(:avatar, upload.id) }
before do
stub_current_geo_node(primary_node) it 'downloads the file from the primary' do
stub_geo_file_transfer(:avatar, upload)
expect_next_instance_of(Gitlab::Geo::Replication::FileTransfer) do |instance|
expect(instance).to receive(:download_from_primary).and_call_original
end
expect(downloader.execute).to have_attributes(success: true)
end
end
context 'on object storage' do
before do
stub_uploads_object_storage(AvatarUploader, direct_upload: true)
end
let!(:upload) { create(:upload, :object_storage) }
subject(:downloader) { described_class.new(:avatar, upload.id) }
it 'streams the upload file from the primary to object storage' do
stub_geo_file_transfer_object_storage(:avatar, upload)
expect_next_instance_of(Gitlab::Geo::Replication::FileTransfer) do |instance|
expect(instance).to receive(:stream_from_primary_to_object_storage).and_call_original
end
expect(downloader.execute).to have_attributes(success: true)
end
context 'with object storage sync disabled' do
before do
secondary.update_column(:sync_object_storage, false)
end
it 'returns a result indicating a failure before a transfer was attempted' do
result = downloader.execute
expect(result).to have_attributes(
success: false,
failed_before_transfer: true,
reason: 'Skipping transfer as this secondary node is not allowed to replicate content on Object Storage'
)
end
end
context 'with object storage disabled' do
before do
stub_uploads_object_storage(AvatarUploader, enabled: false)
end
it 'returns a result indicating a failure before a transfer was attempted' do
result = downloader.execute
expect(result).to have_attributes(
success: false,
failed_before_transfer: true,
reason: 'Skipping transfer as this secondary node is not configured to store avatar on Object Storage'
)
end
end
end
end end
it 'fails to download the file' do context 'with unknown object ID' do
expect(subject.success).to be_falsey let(:unknown_id) { Upload.maximum(:id).to_i + 1 }
expect(subject.primary_missing_file).to be_falsey
subject(:downloader) { described_class.new(:avatar, unknown_id) }
it 'returns a result indicating a failure before a transfer was attempted' do
result = downloader.execute
expect(result).to have_attributes(
success: false,
failed_before_transfer: true,
reason: "Skipping transfer as the avatar (ID = #{unknown_id}) could not be found"
)
end
end end
end
context 'when in a secondary geo node' do context 'when the upload parent object does not exist' do
context 'with local storage only' do let(:upload) { create(:upload) }
let(:secondary_node) { create(:geo_node, :local_storage_only) }
before do subject(:downloader) { described_class.new(:avatar, upload.id) }
stub_current_geo_node(secondary_node)
stub_geo_file_transfer(:file, upload) before do
upload.update_columns(model_id: nil, model_type: nil)
end end
it 'downloads the file' do it 'returns a result indicating a failure before a transfer was attempted' do
expect(subject.success).to be_truthy result = downloader.execute
expect(subject.primary_missing_file).to be_falsey
expect(result).to have_attributes(
success: true,
primary_missing_file: true # FIXME: https://gitlab.com/gitlab-org/gitlab/-/issues/220855
)
end end
end end
end end
...@@ -48,7 +127,10 @@ RSpec.describe Gitlab::Geo::Replication::FileDownloader, :geo do ...@@ -48,7 +127,10 @@ RSpec.describe Gitlab::Geo::Replication::FileDownloader, :geo do
def stub_geo_file_transfer_object_storage(file_type, upload) def stub_geo_file_transfer_object_storage(file_type, upload)
url = primary_node.geo_transfers_url(file_type, upload.id.to_s) url = primary_node.geo_transfers_url(file_type, upload.id.to_s)
redirection = upload.retrieve_uploader.url
file = fixture_file_upload('spec/fixtures/dk.png')
stub_request(:get, url).to_return(status: 307, body: upload.retrieve_uploader.url, headers: {}) stub_request(:get, url).to_return(status: 307, headers: { location: redirection })
stub_request(:get, redirection).to_return(status: 200, body: file.read, headers: {})
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