2018-02-21 21:09:53 +00:00
# frozen_string_literal: true
2020-02-19 18:09:10 +00:00
# rubocop:disable Scalability/IdempotentWorker
2018-02-21 21:09:53 +00:00
module ObjectStorage
class MigrateUploadsWorker
include ApplicationWorker
include ObjectStorageQueue
2019-10-18 11:11:44 +00:00
feature_category_not_owned!
2020-06-12 12:08:56 +00:00
loggable_arguments 0 , 1 , 2 , 3
2019-10-18 11:11:44 +00:00
2018-02-21 21:09:53 +00:00
SanityCheckError = Class . new ( StandardError )
class MigrationResult
attr_reader :upload
attr_accessor :error
def initialize ( upload , error = nil )
2021-04-19 21:09:27 +00:00
@upload = upload
@error = error
2018-02-21 21:09:53 +00:00
end
def success?
error . nil?
end
def to_s
2019-04-10 13:30:09 +00:00
success? ? _ ( " Migration successful. " ) : _ ( " Error while migrating %{upload_id}: %{error_message} " ) % { upload_id : upload . id , error_message : error . message }
2018-02-21 21:09:53 +00:00
end
end
module Report
class MigrationFailures < StandardError
attr_reader :errors
def initialize ( errors )
@errors = errors
end
def message
errors . map ( & :message ) . join ( " \n " )
end
end
def report! ( results )
success , failures = results . partition ( & :success? )
2020-09-08 09:08:31 +00:00
Gitlab :: AppLogger . info header ( success , failures )
Gitlab :: AppLogger . warn failures ( failures )
2018-02-21 21:09:53 +00:00
raise MigrationFailures . new ( failures . map ( & :error ) ) if failures . any?
end
def header ( success , failures )
2019-04-10 13:30:09 +00:00
_ ( " Migrated %{success_count}/%{total_count} files. " ) % { success_count : success . count , total_count : success . count + failures . count }
2018-02-21 21:09:53 +00:00
end
def failures ( failures )
failures . map { | f | " \t #{ f } " } . join ( '\n' )
end
end
include Report
2018-08-27 15:31:01 +00:00
# rubocop: disable CodeReuse/ActiveRecord
2018-03-29 14:45:36 +00:00
def self . enqueue! ( uploads , model_class , mounted_as , to_store )
sanity_check! ( uploads , model_class , mounted_as )
2018-02-21 21:09:53 +00:00
2018-03-29 14:45:36 +00:00
perform_async ( uploads . ids , model_class . to_s , mounted_as , to_store )
2018-02-21 21:09:53 +00:00
end
2018-08-27 15:31:01 +00:00
# rubocop: enable CodeReuse/ActiveRecord
2018-02-21 21:09:53 +00:00
# We need to be sure all the uploads are for the same uploader and model type
# and that the mount point exists if provided.
#
2018-03-29 14:45:36 +00:00
def self . sanity_check! ( uploads , model_class , mounted_as )
2018-02-21 21:09:53 +00:00
upload = uploads . first
uploader_class = upload . uploader . constantize
uploader_types = uploads . map ( & :uploader ) . uniq
model_types = uploads . map ( & :model_type ) . uniq
model_has_mount = mounted_as . nil? || model_class . uploaders [ mounted_as ] == uploader_class
2019-04-10 13:30:09 +00:00
raise ( SanityCheckError , _ ( " Multiple uploaders found: %{uploader_types} " ) % { uploader_types : uploader_types } ) unless uploader_types . count == 1
raise ( SanityCheckError , _ ( " Multiple model types found: %{model_types} " ) % { model_types : model_types } ) unless model_types . count == 1
raise ( SanityCheckError , _ ( " Mount point %{mounted_as} not found in %{model_class}. " ) % { mounted_as : mounted_as , model_class : model_class } ) unless model_has_mount
2018-02-21 21:09:53 +00:00
end
2018-08-27 15:31:01 +00:00
# rubocop: disable CodeReuse/ActiveRecord
2018-03-30 12:52:37 +00:00
def perform ( * args )
args_check! ( args )
( ids , model_type , mounted_as , to_store ) = args
2018-03-29 14:45:36 +00:00
@model_class = model_type . constantize
2018-02-21 21:09:53 +00:00
@mounted_as = mounted_as & . to_sym
@to_store = to_store
uploads = Upload . preload ( :model ) . where ( id : ids )
sanity_check! ( uploads )
results = migrate ( uploads )
report! ( results )
rescue SanityCheckError = > e
# do not retry: the job is insane
2020-09-08 09:08:31 +00:00
Gitlab :: AppLogger . warn " #{ self . class } : Sanity check error ( #{ e . message } ) "
2018-02-21 21:09:53 +00:00
end
2018-08-27 15:31:01 +00:00
# rubocop: enable CodeReuse/ActiveRecord
2018-02-21 21:09:53 +00:00
def sanity_check! ( uploads )
2018-03-29 14:45:36 +00:00
self . class . sanity_check! ( uploads , @model_class , @mounted_as )
2018-02-21 21:09:53 +00:00
end
2018-03-30 12:52:37 +00:00
def args_check! ( args )
return if args . count == 4
case args . count
2019-04-10 13:30:09 +00:00
when 3 then raise SanityCheckError , _ ( " Job is missing the `model_type` argument. " )
2018-03-30 12:52:37 +00:00
else
2019-04-10 13:30:09 +00:00
raise SanityCheckError , _ ( " Job has wrong arguments format. " )
2018-03-30 12:52:37 +00:00
end
end
2018-02-21 21:09:53 +00:00
def build_uploaders ( uploads )
2019-10-03 00:05:59 +00:00
uploads . map { | upload | upload . retrieve_uploader ( @mounted_as ) }
2018-02-21 21:09:53 +00:00
end
def migrate ( uploads )
build_uploaders ( uploads ) . map ( & method ( :process_uploader ) )
end
def process_uploader ( uploader )
MigrationResult . new ( uploader . upload ) . tap do | result |
2019-03-13 13:42:43 +00:00
uploader . migrate! ( @to_store )
rescue = > e
result . error = e
2018-02-21 21:09:53 +00:00
end
end
end
end
2020-02-19 18:09:10 +00:00
# rubocop:enable Scalability/IdempotentWorker