2018-12-23 12:14:25 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
module Gitlab
|
|
|
|
module Verify
|
|
|
|
class BatchVerifier
|
|
|
|
attr_reader :batch_size, :start, :finish
|
|
|
|
|
|
|
|
def initialize(batch_size:, start: nil, finish: nil)
|
|
|
|
@batch_size = batch_size
|
|
|
|
@start = start
|
|
|
|
@finish = finish
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
fix_google_api_logger
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
# Yields a Range of IDs and a Hash of failed verifications (object => error)
|
|
|
|
def run_batches(&blk)
|
2018-11-08 19:23:39 +05:30
|
|
|
all_relation.in_batches(of: batch_size, start: start, finish: finish) do |batch| # rubocop: disable Cop/InBatches
|
|
|
|
range = batch.first.id..batch.last.id
|
|
|
|
failures = run_batch_for(batch)
|
2018-03-27 19:54:05 +05:30
|
|
|
|
|
|
|
yield(range, failures)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def name
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
def describe(_object)
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
def run_batch_for(batch)
|
|
|
|
batch.map { |upload| verify(upload) }.compact.to_h
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def verify(object)
|
2018-11-08 19:23:39 +05:30
|
|
|
local?(object) ? verify_local(object) : verify_remote(object)
|
|
|
|
rescue => err
|
|
|
|
failure(object, err.inspect)
|
|
|
|
end
|
|
|
|
|
|
|
|
def verify_local(object)
|
2018-03-27 19:54:05 +05:30
|
|
|
expected = expected_checksum(object)
|
|
|
|
actual = actual_checksum(object)
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
return failure(object, 'Checksum missing') unless expected.present?
|
|
|
|
return failure(object, 'Checksum mismatch') unless expected == actual
|
|
|
|
|
|
|
|
success
|
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
# We don't calculate checksum for remote objects, so just check existence
|
|
|
|
def verify_remote(object)
|
|
|
|
return failure(object, 'Remote object does not exist') unless remote_object_exists?(object)
|
|
|
|
|
|
|
|
success
|
|
|
|
end
|
|
|
|
|
|
|
|
def success
|
2018-03-27 19:54:05 +05:30
|
|
|
nil
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def failure(object, message)
|
|
|
|
[object, message]
|
|
|
|
end
|
|
|
|
|
|
|
|
# It's already set to Logger::INFO, but acts as if it is set to
|
|
|
|
# Logger::DEBUG, and this fixes it...
|
|
|
|
def fix_google_api_logger
|
|
|
|
if Object.const_defined?('Google::Apis')
|
|
|
|
Google::Apis.logger.level = Logger::INFO
|
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
# This should return an ActiveRecord::Relation suitable for calling #in_batches on
|
2018-11-08 19:23:39 +05:30
|
|
|
def all_relation
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
# Should return true if the object is stored locally
|
|
|
|
def local?(_object)
|
2018-03-27 19:54:05 +05:30
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
# The checksum we expect the object to have
|
|
|
|
def expected_checksum(_object)
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
|
|
|
|
|
|
|
# The freshly-recalculated checksum of the object
|
|
|
|
def actual_checksum(_object)
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
# Be sure to perform a hard check of the remote object (don't just check DB value)
|
|
|
|
def remote_object_exists?(object)
|
|
|
|
raise NotImplementedError.new
|
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|