Commit 0faff42d authored by Bob Van Landuyt's avatar Bob Van Landuyt Committed by Bob Van Landuyt

Add methods to revert project renames

parent 3e84b633
......@@ -7,6 +7,7 @@ module Gitlab
delegate :update_column_in_batches,
:replace_sql,
:say,
to: :migration
def initialize(paths, migration)
......@@ -26,13 +27,18 @@ module Gitlab
new_path = rename_path(namespace_path, old_path)
new_full_path = join_routable_path(namespace_path, new_path)
perform_rename(routable, old_full_path, new_full_path)
[old_full_path, new_full_path]
end
def perform_rename(routable, old_full_path, new_full_path)
# skips callbacks & validations
routable.class.where(id: routable)
.update_all(path: new_path)
new_path = new_full_path.split('/').last
routable.class.where(id: routable).
update_all(path: new_path)
rename_routes(old_full_path, new_full_path)
[old_full_path, new_full_path]
end
def rename_routes(old_full_path, new_full_path)
......@@ -86,7 +92,10 @@ module Gitlab
def move_folders(directory, old_relative_path, new_relative_path)
old_path = File.join(directory, old_relative_path)
return unless File.directory?(old_path)
unless File.directory?(old_path)
say "#{old_path} doesn't exist, skipping"
return
end
new_path = File.join(directory, new_relative_path)
FileUtils.mv(old_path, new_path)
......@@ -115,10 +124,25 @@ module Gitlab
end
def track_rename(type, old_path, new_path)
key = "rename:#{migration.version}:#{type}"
key = redis_key_for_type(type)
Gitlab::Redis.with { |redis| redis.lpush(key, [old_path, new_path].to_json) }
end
def reverts_for_type(type)
key = redis_key_for_type(type)
Gitlab::Redis.with do |redis|
while rename_info = redis.lpop(key)
path_before_rename, path_after_rename = JSON.parse(rename_info)
say "renaming #{type} from #{path_after_rename} back to #{path_before_rename}"
yield(path_before_rename, path_after_rename)
end
end
end
def redis_key_for_type(type)
"rename:#{migration.version}:#{type}"
end
def file_storage?
CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
end
......
......@@ -18,12 +18,31 @@ module Gitlab
track_rename('project', old_full_path, new_full_path)
move_project_folders(project, old_full_path, new_full_path)
end
def move_project_folders(project, old_full_path, new_full_path)
move_repository(project, old_full_path, new_full_path)
move_repository(project, "#{old_full_path}.wiki", "#{new_full_path}.wiki")
move_uploads(old_full_path, new_full_path)
move_pages(old_full_path, new_full_path)
end
def revert_renames
reverts_for_type('project') do |path_before_rename, current_path|
matches_path = MigrationClasses::Route.arel_table[:path].matches(current_path)
project = MigrationClasses::Project.joins(:route)
.where(matches_path).first
if project
perform_rename(project, current_path, path_before_rename)
move_project_folders(project, current_path, path_before_rename)
else
say "Couldn't rename project##{project.id} from #{current_path} back to #{path_before_rename}, project no longer exists"
end
end
end
def move_repository(project, old_path, new_path)
unless gitlab_shell.mv_repository(project.repository_storage_path,
old_path,
......
......@@ -153,6 +153,30 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca
end
end
describe '#perform_rename' do
describe 'for namespaces' do
let(:namespace) { create(:namespace, path: 'the-path') }
it 'renames the path' do
subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed')
expect(namespace.reload.path).to eq('renamed')
end
it 'renames all the routes for the namespace' do
child = create(:group, path: 'child', parent: namespace)
project = create(:project, namespace: child, path: 'the-project')
other_one = create(:namespace, path: 'the-path-is-similar')
subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed')
expect(namespace.reload.route.path).to eq('renamed')
expect(child.reload.route.path).to eq('renamed/child')
expect(project.reload.route.path).to eq('renamed/child/the-project')
expect(other_one.reload.route.path).to eq('the-path-is-similar')
end
end
end
describe '#move_pages' do
it 'moves the pages directory' do
expect(subject).to receive(:move_folders)
......@@ -220,4 +244,17 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca
expect(new_path).to eq('path/to/renamed')
end
end
describe '#reverts_for_type', redis: true do
it 'yields for each tracked rename' do
subject.track_rename('project', 'old_path', 'new_path')
subject.track_rename('project', 'old_path2', 'new_path2')
subject.track_rename('namespace', 'namespace_path', 'new_namespace_path')
expect { |b| subject.reverts_for_type('project', &b) }
.to yield_successive_args(%w(old_path2 new_path2), %w(old_path new_path))
expect { |b| subject.reverts_for_type('namespace', &b) }
.to yield_with_args('namespace_path', 'new_namespace_path')
end
end
end
......@@ -3,6 +3,12 @@ require 'spec_helper'
describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :truncate do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
let(:project) do
create(:empty_project,
path: 'the-path',
namespace: create(:namespace, path: 'known-parent' ))
end
before do
allow(migration).to receive(:say)
......@@ -47,12 +53,6 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :tr
end
describe '#rename_project' do
let(:project) do
create(:empty_project,
path: 'the-path',
namespace: create(:namespace, path: 'known-parent' ))
end
it 'renames path & route for the project' do
expect(subject).to receive(:rename_path_for_routable)
.with(project)
......@@ -63,34 +63,42 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :tr
expect(project.reload.path).to eq('the-path0')
end
it 'tracks the rename' do
expect(subject).to receive(:track_rename)
.with('project', 'known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
it 'renames the folders for the project' do
expect(subject).to receive(:move_project_folders).with(project, 'known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
end
describe '#move_project_folders' do
it 'moves the wiki & the repo' do
expect(subject).to receive(:move_repository)
.with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki')
expect(subject).to receive(:move_repository)
.with(project, 'known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
subject.move_project_folders(project, 'known-parent/the-path', 'known-parent/the-path0')
end
it 'moves uploads' do
expect(subject).to receive(:move_uploads)
.with('known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
subject.move_project_folders(project, 'known-parent/the-path', 'known-parent/the-path0')
end
it 'moves pages' do
expect(subject).to receive(:move_pages)
.with('known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
it 'tracks the rename' do
expect(subject).to receive(:track_rename)
.with('project', 'known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
subject.move_project_folders(project, 'known-parent/the-path', 'known-parent/the-path0')
end
end
......@@ -106,4 +114,40 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :tr
expect(File.directory?(expected_path)).to be(true)
end
end
describe '#revert_renames', redis: true do
it 'renames the routes back to the previous values' do
subject.rename_project(project)
expect(subject).to receive(:perform_rename)
.with(
kind_of(Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses::Project),
'known-parent/the-path0',
'known-parent/the-path'
).and_call_original
subject.revert_renames
expect(project.reload.path).to eq('the-path')
expect(project.route.path).to eq('known-parent/the-path')
end
it 'moves the repositories back to their original place' do
project.create_repository
subject.rename_project(project)
expected_path = File.join(TestEnv.repos_path, 'known-parent', 'the-path.git')
expect(subject).to receive(:move_project_folders)
.with(
kind_of(Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses::Project),
'known-parent/the-path0',
'known-parent/the-path'
).and_call_original
subject.revert_renames
expect(File.directory?(expected_path)).to be_truthy
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment