2018-07-02 11:09:49 -04:00
|
|
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
|
|
|
# for more information on how to write migrations for GitLab.
|
|
|
|
|
|
|
|
# This migration generates missing routes for any projects and namespaces that
|
|
|
|
# don't already have a route.
|
|
|
|
#
|
|
|
|
# On GitLab.com this would insert 611 project routes, and 0 namespace routes.
|
|
|
|
# The exact number could vary per instance, so we take care of both just in
|
|
|
|
# case.
|
2018-11-13 02:27:31 -05:00
|
|
|
class GenerateMissingRoutes < ActiveRecord::Migration[4.2]
|
2018-07-02 11:09:49 -04:00
|
|
|
include Gitlab::Database::MigrationHelpers
|
|
|
|
|
|
|
|
DOWNTIME = false
|
|
|
|
|
|
|
|
disable_ddl_transaction!
|
|
|
|
|
|
|
|
class User < ActiveRecord::Base
|
|
|
|
self.table_name = 'users'
|
|
|
|
end
|
|
|
|
|
|
|
|
class Route < ActiveRecord::Base
|
|
|
|
self.table_name = 'routes'
|
|
|
|
end
|
|
|
|
|
|
|
|
module Routable
|
|
|
|
def build_full_path
|
|
|
|
if parent && path
|
|
|
|
parent.build_full_path + '/' + path
|
|
|
|
else
|
|
|
|
path
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_full_name
|
|
|
|
if parent && name
|
|
|
|
parent.human_name + ' / ' + name
|
|
|
|
else
|
|
|
|
name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def human_name
|
|
|
|
build_full_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def attributes_for_insert
|
|
|
|
time = Time.zone.now
|
|
|
|
|
|
|
|
{
|
|
|
|
# We can't use "self.class.name" here as that would include the
|
|
|
|
# migration namespace.
|
|
|
|
source_type: source_type_for_route,
|
|
|
|
source_id: id,
|
|
|
|
created_at: time,
|
|
|
|
updated_at: time,
|
|
|
|
name: build_full_name,
|
|
|
|
|
|
|
|
# The route path might already be taken. Instead of trying to generate a
|
|
|
|
# new unique name on every conflict, we just append the row ID to the
|
|
|
|
# route path.
|
|
|
|
path: "#{build_full_path}-#{id}"
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Project < ActiveRecord::Base
|
|
|
|
self.table_name = 'projects'
|
|
|
|
|
|
|
|
include EachBatch
|
|
|
|
include GenerateMissingRoutes::Routable
|
|
|
|
|
|
|
|
belongs_to :namespace, class_name: 'GenerateMissingRoutes::Namespace'
|
|
|
|
|
|
|
|
has_one :route,
|
|
|
|
as: :source,
|
|
|
|
inverse_of: :source,
|
|
|
|
class_name: 'GenerateMissingRoutes::Route'
|
|
|
|
|
|
|
|
alias_method :parent, :namespace
|
|
|
|
alias_attribute :parent_id, :namespace_id
|
|
|
|
|
|
|
|
def self.without_routes
|
|
|
|
where(
|
|
|
|
'NOT EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM routes
|
|
|
|
WHERE source_type = ?
|
|
|
|
AND source_id = projects.id
|
|
|
|
)',
|
|
|
|
'Project'
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def source_type_for_route
|
|
|
|
'Project'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Namespace < ActiveRecord::Base
|
|
|
|
self.table_name = 'namespaces'
|
|
|
|
|
|
|
|
include EachBatch
|
|
|
|
include GenerateMissingRoutes::Routable
|
|
|
|
|
|
|
|
belongs_to :parent, class_name: 'GenerateMissingRoutes::Namespace'
|
|
|
|
belongs_to :owner, class_name: 'GenerateMissingRoutes::User'
|
|
|
|
|
|
|
|
has_one :route,
|
|
|
|
as: :source,
|
|
|
|
inverse_of: :source,
|
|
|
|
class_name: 'GenerateMissingRoutes::Route'
|
|
|
|
|
|
|
|
def self.without_routes
|
|
|
|
where(
|
|
|
|
'NOT EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM routes
|
|
|
|
WHERE source_type = ?
|
|
|
|
AND source_id = namespaces.id
|
|
|
|
)',
|
|
|
|
'Namespace'
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def source_type_for_route
|
|
|
|
'Namespace'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def up
|
|
|
|
[Namespace, Project].each do |model|
|
|
|
|
model.without_routes.each_batch(of: 100) do |batch|
|
|
|
|
rows = batch.map(&:attributes_for_insert)
|
|
|
|
|
|
|
|
Gitlab::Database.bulk_insert(:routes, rows)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def down
|
|
|
|
# Removing routes we previously generated makes no sense.
|
|
|
|
end
|
|
|
|
end
|