2018-03-17 18:26:18 +05:30
|
|
|
# rubocop:disable Migration/UpdateLargeTable
|
2017-08-17 22:00:37 +05:30
|
|
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
|
|
|
# for more information on how to write migrations for GitLab.
|
|
|
|
|
2019-01-03 12:48:30 +05:30
|
|
|
class FixWronglyRenamedRoutes < ActiveRecord::Migration
|
2017-08-17 22:00:37 +05:30
|
|
|
include Gitlab::Database::RenameReservedPathsMigration::V1
|
|
|
|
|
|
|
|
DOWNTIME = false
|
|
|
|
|
|
|
|
disable_ddl_transaction!
|
|
|
|
|
|
|
|
DISALLOWED_ROOT_PATHS = %w[
|
|
|
|
-
|
|
|
|
abuse_reports
|
|
|
|
api
|
|
|
|
autocomplete
|
|
|
|
explore
|
|
|
|
health_check
|
|
|
|
import
|
|
|
|
invites
|
|
|
|
jwt
|
|
|
|
koding
|
|
|
|
member
|
|
|
|
notification_settings
|
|
|
|
oauth
|
|
|
|
sent_notifications
|
|
|
|
unicorn_test
|
|
|
|
uploads
|
|
|
|
users
|
|
|
|
]
|
|
|
|
|
|
|
|
FIXED_PATHS = DISALLOWED_ROOT_PATHS.map { |p| "#{p}0" }
|
|
|
|
|
|
|
|
class Route < Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses::Route
|
|
|
|
self.table_name = 'routes'
|
|
|
|
end
|
|
|
|
|
|
|
|
def routes
|
|
|
|
@routes ||= Route.arel_table
|
|
|
|
end
|
|
|
|
|
|
|
|
def namespaces
|
|
|
|
@namespaces ||= Arel::Table.new(:namespaces)
|
|
|
|
end
|
|
|
|
|
|
|
|
def wildcard_collection(collection)
|
|
|
|
collection.map { |word| "#{word}%" }
|
|
|
|
end
|
|
|
|
|
|
|
|
# The routes that got incorrectly renamed before, still have a namespace that
|
|
|
|
# contains the correct path.
|
|
|
|
# This query fetches all rows from the `routes` table that meet the following
|
|
|
|
# conditions using `api` as an example:
|
|
|
|
# - route.path ILIKE `api0%`
|
|
|
|
# - route.source_type = `Namespace`
|
|
|
|
# - namespace.parent_id IS NULL
|
|
|
|
# - namespace.path ILIKE `api%`
|
|
|
|
# - NOT(namespace.path ILIKE `api0%`)
|
|
|
|
# This gives us all root-routes, that were renamed, but their namespace was not.
|
|
|
|
#
|
|
|
|
def wrongly_renamed
|
|
|
|
Route.joins("INNER JOIN namespaces ON routes.source_id = namespaces.id")
|
|
|
|
.where(
|
|
|
|
routes[:source_type].eq('Namespace')
|
|
|
|
.and(namespaces[:parent_id].eq(nil))
|
|
|
|
)
|
|
|
|
.where(namespaces[:path].matches_any(wildcard_collection(DISALLOWED_ROOT_PATHS)))
|
|
|
|
.where.not(namespaces[:path].matches_any(wildcard_collection(FIXED_PATHS)))
|
|
|
|
.where(routes[:path].matches_any(wildcard_collection(FIXED_PATHS)))
|
|
|
|
end
|
|
|
|
|
|
|
|
# Using the query above, we just fetch the `route.path` & the `namespace.path`
|
|
|
|
# `route.path` is the part of the route that is now incorrect
|
|
|
|
# `namespace.path` is what it should be
|
|
|
|
# We can use `route.path` to find all the namespaces that need to be fixed
|
|
|
|
# And we can use `namespace.path` to apply the correct name.
|
|
|
|
#
|
|
|
|
def paths_and_corrections
|
|
|
|
connection.select_all(
|
|
|
|
wrongly_renamed.select(routes[:path], namespaces[:path].as('namespace_path')).to_sql
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
# This can be used to limit the `update_in_batches` call to all routes for a
|
|
|
|
# single namespace, note the `/` that's what went wrong in the initial migration.
|
|
|
|
#
|
|
|
|
def routes_in_namespace_query(namespace)
|
|
|
|
routes[:path].matches_any([namespace, "#{namespace}/%"])
|
|
|
|
end
|
|
|
|
|
|
|
|
def up
|
|
|
|
paths_and_corrections.each do |root_namespace|
|
|
|
|
wrong_path = root_namespace['path']
|
|
|
|
correct_path = root_namespace['namespace_path']
|
|
|
|
replace_statement = replace_sql(Route.arel_table[:path], wrong_path, correct_path)
|
|
|
|
|
|
|
|
update_column_in_batches(:routes, :path, replace_statement) do |table, query|
|
|
|
|
query.where(routes_in_namespace_query(wrong_path))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def down
|
|
|
|
end
|
|
|
|
end
|