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
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class FixWronglyRenamedRoutes < ActiveRecord::Migration
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