gitlab-org--gitlab-foss/db/post_migrate/20161221153951_rename_reserved_project_names.rb
Gabriel Mazetto d391dfb4ac Refactored AfterRenameService to reduce coupling
We still rely on the Dirty API for project rename (before/after) values,
but we don't access the dirty api from the service class anymore.

The previous value is now part of the initialization, which makes it
easier to test and the behavior is clearer.

The same was done with the `rename_repo` on the Storage classes, we now
provide before and after values as part of the method signature.
2019-01-22 13:55:18 +01:00

137 lines
3.3 KiB
Ruby

class RenameReservedProjectNames < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
THREAD_COUNT = 8
KNOWN_PATHS = %w(.well-known
all
blame
blob
commits
create
create_dir
edit
files
find_file
groups
hooks
issues
logs_tree
merge_requests
new
preview
projects
raw
repository
robots.txt
s
snippets
teams
tree
u
unsubscribes
update
users
wikis).freeze
def up
queues = Array.new(THREAD_COUNT) { Queue.new }
start = false
threads = Array.new(THREAD_COUNT) do |index|
Thread.new do
queue = queues[index]
# Wait until we have input to process.
until start; end
rename_projects(queue.pop) until queue.empty?
end
end
enum = queues.each
reserved_projects.each_slice(100) do |slice|
begin
queue = enum.next
rescue StopIteration
enum.rewind
retry
end
queue << slice
end
start = true
threads.each(&:join)
end
def down
# nothing to do here
end
private
def reserved_projects
Project.unscoped
.includes(:namespace)
.where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
.where('projects.path' => KNOWN_PATHS)
end
def route_exists?(full_path)
quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
ActiveRecord::Base.connection
.select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
end
# Adds number to the end of the path that is not taken by other route
def rename_path(namespace_path, path_was)
counter = 0
path = "#{path_was}#{counter}"
while route_exists?("#{namespace_path}/#{path}")
counter += 1
path = "#{path_was}#{counter}"
end
path
end
def rename_projects(projects)
projects.each do |project|
id = project.id
path_was = project.path
namespace_path = project.namespace.path
path = rename_path(namespace_path, path_was)
begin
# Because project path update is quite complex operation we can't safely
# copy-paste all code from GitLab. As exception we use Rails code here
if rename_project_row(project, path)
after_rename_service(project, path_was, namespace_path).execute
end
rescue Exception => e # rubocop: disable Lint/RescueException
Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
end
end
end
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
project.update(path: path) &&
defined?(Projects::AfterRenameService)
end
def after_rename_service(project, path_was, namespace_path)
AfterRenameService.new(
project,
path_before: path_was,
full_path_before: "#{namespace_path}/#{path_was}"
).execute
end
end