diff options
Diffstat (limited to 'app/services/lfs/push_service.rb')
-rw-r--r-- | app/services/lfs/push_service.rb | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/app/services/lfs/push_service.rb b/app/services/lfs/push_service.rb new file mode 100644 index 00000000000..6e1a11ebff8 --- /dev/null +++ b/app/services/lfs/push_service.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Lfs + # Lfs::PushService pushes the LFS objects associated with a project to a + # remote URL + class PushService < BaseService + include Gitlab::Utils::StrongMemoize + + # Match the canonical LFS client's batch size: + # https://github.com/git-lfs/git-lfs/blob/master/tq/transfer_queue.go#L19 + BATCH_SIZE = 100 + + def execute + lfs_objects_relation.each_batch(of: BATCH_SIZE) do |objects| + push_objects(objects) + end + + success + rescue => err + error(err.message) + end + + private + + # Currently we only set repository_type for design repository objects, so + # push mirroring must send objects with a `nil` repository type - but if the + # wiki repository uses LFS, its objects will also be sent. This will be + # addressed by https://gitlab.com/gitlab-org/gitlab/-/issues/250346 + def lfs_objects_relation + project.lfs_objects_for_repository_types(nil, :project) + end + + def push_objects(objects) + rsp = lfs_client.batch('upload', objects) + objects = objects.index_by(&:oid) + + rsp.fetch('objects', []).each do |spec| + actions = spec['actions'] + object = objects[spec['oid']] + + upload_object!(object, spec) if actions&.key?('upload') + verify_object!(object, spec) if actions&.key?('verify') + end + end + + def upload_object!(object, spec) + authenticated = spec['authenticated'] + upload = spec.dig('actions', 'upload') + + # The server wants us to upload the object but something is wrong + unless object && object.size == spec['size'].to_i + log_error("Couldn't match object #{spec['oid']}/#{spec['size']}") + return + end + + lfs_client.upload(object, upload, authenticated: authenticated) + end + + def verify_object!(object, spec) + # TODO: the remote has requested that we make another call to verify that + # the object has been sent correctly. + # https://gitlab.com/gitlab-org/gitlab/-/issues/250654 + log_error("LFS upload verification requested, but not supported for #{object.oid}") + end + + def url + params.fetch(:url) + end + + def credentials + params.fetch(:credentials) + end + + def lfs_client + strong_memoize(:lfs_client) do + Gitlab::Lfs::Client.new(url, credentials: credentials) + end + end + end +end |