122 lines
3.1 KiB
Ruby
122 lines
3.1 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
module RepositoryStorageMovable
|
||
|
extend ActiveSupport::Concern
|
||
|
include AfterCommitQueue
|
||
|
|
||
|
included do
|
||
|
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
||
|
|
||
|
validates :container, presence: true
|
||
|
validates :state, presence: true
|
||
|
validates :source_storage_name,
|
||
|
on: :create,
|
||
|
presence: true,
|
||
|
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
||
|
validates :destination_storage_name,
|
||
|
on: :create,
|
||
|
presence: true,
|
||
|
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
||
|
validate :container_repository_writable, on: :create
|
||
|
|
||
|
default_value_for(:destination_storage_name, allows_nil: false) do
|
||
|
pick_repository_storage
|
||
|
end
|
||
|
|
||
|
state_machine initial: :initial do
|
||
|
event :schedule do
|
||
|
transition initial: :scheduled
|
||
|
end
|
||
|
|
||
|
event :start do
|
||
|
transition scheduled: :started
|
||
|
end
|
||
|
|
||
|
event :finish_replication do
|
||
|
transition started: :replicated
|
||
|
end
|
||
|
|
||
|
event :finish_cleanup do
|
||
|
transition replicated: :finished
|
||
|
end
|
||
|
|
||
|
event :do_fail do
|
||
|
transition [:initial, :scheduled, :started] => :failed
|
||
|
transition replicated: :cleanup_failed
|
||
|
end
|
||
|
|
||
|
around_transition initial: :scheduled do |storage_move, block|
|
||
|
block.call
|
||
|
|
||
|
begin
|
||
|
storage_move.container.set_repository_read_only!(skip_git_transfer_check: true)
|
||
|
rescue => err
|
||
|
storage_move.add_error(err.message)
|
||
|
next false
|
||
|
end
|
||
|
|
||
|
storage_move.run_after_commit do
|
||
|
storage_move.schedule_repository_storage_update_worker
|
||
|
end
|
||
|
|
||
|
true
|
||
|
end
|
||
|
|
||
|
before_transition started: :replicated do |storage_move|
|
||
|
storage_move.container.set_repository_writable!
|
||
|
|
||
|
storage_move.update_repository_storage(storage_move.destination_storage_name)
|
||
|
end
|
||
|
|
||
|
before_transition started: :failed do |storage_move|
|
||
|
storage_move.container.set_repository_writable!
|
||
|
end
|
||
|
|
||
|
state :initial, value: 1
|
||
|
state :scheduled, value: 2
|
||
|
state :started, value: 3
|
||
|
state :finished, value: 4
|
||
|
state :failed, value: 5
|
||
|
state :replicated, value: 6
|
||
|
state :cleanup_failed, value: 7
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class_methods do
|
||
|
private
|
||
|
|
||
|
def pick_repository_storage
|
||
|
container_klass = reflect_on_association(:container).class_name.constantize
|
||
|
|
||
|
container_klass.pick_repository_storage
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Projects, snippets, and group wikis has different db structure. In projects,
|
||
|
# we need to update some columns in this step, but we don't with the other resources.
|
||
|
#
|
||
|
# Therefore, we create this No-op method for snippets and wikis and let project
|
||
|
# overwrite it in their implementation.
|
||
|
def update_repository_storage(new_storage)
|
||
|
# No-op
|
||
|
end
|
||
|
|
||
|
def schedule_repository_storage_update_worker
|
||
|
raise NotImplementedError
|
||
|
end
|
||
|
|
||
|
def add_error(message)
|
||
|
errors.add(error_key, message)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def container_repository_writable
|
||
|
add_error(_('is read only')) if container&.repository_read_only?
|
||
|
end
|
||
|
|
||
|
def error_key
|
||
|
raise NotImplementedError
|
||
|
end
|
||
|
end
|