File: bulk_import.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (129 lines) | stat: -rw-r--r-- 3,464 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# frozen_string_literal: true

# The BulkImport model links all models required for a bulk import of groups and
# projects to a GitLab instance. It associates the import with the responsible
# user.
class BulkImport < ApplicationRecord
  include AfterCommitQueue

  MIN_MAJOR_VERSION = 14
  MIN_MINOR_VERSION_FOR_PROJECT = 4

  belongs_to :user, optional: false

  has_one :configuration, class_name: 'BulkImports::Configuration'
  has_many :entities, class_name: 'BulkImports::Entity'

  validates :source_type, :status, presence: true

  enum source_type: { gitlab: 0 }

  scope :stale, -> { where('updated_at < ?', 24.hours.ago).where(status: [0, 1]) }
  scope :order_by_updated_at_and_id, ->(direction) { order(updated_at: direction, id: :asc) }
  scope :order_by_created_at, ->(direction) { order(created_at: direction) }
  scope :with_configuration, -> { includes(:configuration) }

  state_machine :status, initial: :created do
    state :created, value: 0
    state :started, value: 1
    state :finished, value: 2
    state :timeout, value: 3
    state :failed, value: -1
    state :canceled, value: -2

    event :start do
      transition created: :started
    end

    event :finish do
      transition started: :finished
    end

    event :cleanup_stale do
      transition created: :timeout
      transition started: :timeout
    end

    event :fail_op do
      transition any => :failed
    end

    event :cancel do
      transition any => :canceled
    end

    after_transition any => [:finished, :failed, :timeout] do |bulk_import|
      bulk_import.update_has_failures
      bulk_import.send_completion_notification
    end
    after_transition any => [:canceled] do |bulk_import|
      bulk_import.run_after_commit do
        bulk_import.propagate_cancel
      end
    end
  end

  def source_version_info
    Gitlab::VersionInfo.parse(source_version)
  end

  def self.min_gl_version_for_project_migration
    Gitlab::VersionInfo.new(MIN_MAJOR_VERSION, MIN_MINOR_VERSION_FOR_PROJECT)
  end

  def self.min_gl_version_for_migration_in_batches
    Gitlab::VersionInfo.new(16, 2)
  end

  def self.all_human_statuses
    state_machine.states.map(&:human_name)
  end

  def update_has_failures
    return if has_failures
    return unless entities.any?(&:has_failures)

    update!(has_failures: true)
  end

  def propagate_cancel
    return unless entities.any?

    entities.each(&:cancel)
  end

  def supports_batched_export?
    source_version_info >= self.class.min_gl_version_for_migration_in_batches
  end

  def completed?
    finished? || failed? || timeout? || canceled?
  end

  def send_completion_notification
    run_after_commit do
      Notify.bulk_import_complete(user.id, id).deliver_later
    end
  end

  def destination_group_roots
    entities.where(parent: nil).filter_map do |entity|
      entity.group || entity.project
    end.map(&:root_ancestor).uniq
  end

  def namespaces_with_unassigned_placeholders
    namespaces = destination_group_roots
    namespace_ids = namespaces.collect(&:id)

    reassignable_statuses = Import::SourceUser::STATUSES.slice(*Import::SourceUser::REASSIGNABLE_STATUSES).values
    source_users = Import::SourceUser.for_namespace(namespace_ids).by_statuses(reassignable_statuses)
    valid_namespace_ids = source_users.collect(&:namespace_id).uniq

    namespaces.select { |namespace| valid_namespace_ids.include?(namespace.id) }
  end

  def source_url
    configuration&.url
  end
end