summaryrefslogtreecommitdiff
path: root/app/services/lfs/push_service.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/lfs/push_service.rb')
-rw-r--r--app/services/lfs/push_service.rb80
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