Commit 82e6ed31 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Fix incorrect namespaces & route for user-routes

This fixes the `Namespace#name` and `Route#name` for all user
namespaces and their personal projects in case they don't match the
user name anymore.

More info info in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23272
parent f138acb9
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class ScheduleFixingNamesOfUserNamespaces < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
class Namespace < ActiveRecord::Base
include ::EachBatch
self.table_name = 'namespaces'
scope :user_namespaces, -> { where(type: nil) }
end
class Route < ActiveRecord::Base
include ::EachBatch
self.table_name = 'routes'
scope :project_routes, -> { where(source_type: 'Project') }
end
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
ScheduleFixingNamesOfUserNamespaces::Namespace.user_namespaces,
'FixUserNamespaceNames',
60.seconds,
batch_size: 5000
)
queue_background_migration_jobs_by_range_at_intervals(
ScheduleFixingNamesOfUserNamespaces::Route.project_routes,
'FixUserProjectRouteNames',
60.seconds,
batch_size: 5000
)
end
def down
# no-op
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# This migration fixes the namespaces.name for all user-namespaces that have names
# that aren't equal to the users name.
# Then it uses the updated names of the namespaces to update the associated routes
# For more info see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23272
class FixUserNamespaceNames
def perform(from_id, to_id)
fix_namespace_names(from_id, to_id)
fix_namespace_route_names(from_id, to_id)
end
def fix_namespace_names(from_id, to_id)
ActiveRecord::Base.connection.execute <<~UPDATE_NAMESPACES
WITH namespaces_to_update AS (
SELECT
namespaces.id,
users.name AS correct_name
FROM
namespaces
INNER JOIN users ON namespaces.owner_id = users.id
WHERE
namespaces.type IS NULL
AND namespaces.id BETWEEN #{from_id} AND #{to_id}
AND namespaces.name != users.name
)
UPDATE
namespaces
SET
name = correct_name
FROM
namespaces_to_update
WHERE
namespaces.id = namespaces_to_update.id
UPDATE_NAMESPACES
end
def fix_namespace_route_names(from_id, to_id)
ActiveRecord::Base.connection.execute <<~ROUTES_UPDATE
WITH routes_to_update AS (
SELECT
routes.id,
users.name AS correct_name
FROM
routes
INNER JOIN namespaces ON routes.source_id = namespaces.id
INNER JOIN users ON namespaces.owner_id = users.id
WHERE
namespaces.type IS NULL
AND routes.source_type = 'Namespace'
AND namespaces.id BETWEEN #{from_id} AND #{to_id}
AND (routes.name != users.name OR routes.name IS NULL)
)
UPDATE
routes
SET
name = correct_name
FROM
routes_to_update
WHERE
routes_to_update.id = routes.id
ROUTES_UPDATE
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# This migration fixes the routes.name for all user-projects that have names
# that don't start with the users name.
# For more info see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23272
class FixUserProjectRouteNames
def perform(from_id, to_id)
ActiveRecord::Base.connection.execute <<~ROUTES_UPDATE
WITH routes_to_update AS (
SELECT
routes.id,
users.name || ' / ' || projects.name AS correct_name
FROM
routes
INNER JOIN projects ON routes.source_id = projects.id
INNER JOIN namespaces ON projects.namespace_id = namespaces.id
INNER JOIN users ON namespaces.owner_id = users.id
WHERE
routes.source_type = 'Project'
AND routes.id BETWEEN #{from_id} AND #{to_id}
AND namespaces.type IS NULL
AND (routes.name NOT LIKE users.name || '%' OR routes.name IS NULL)
)
UPDATE
routes
SET
name = routes_to_update.correct_name
FROM
routes_to_update
WHERE
routes_to_update.id = routes.id
ROUTES_UPDATE
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::FixUserNamespaceNames, :migration, schema: 20190620112608 do
let(:namespaces) { table(:namespaces) }
let(:users) { table(:users) }
let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
context 'updating the namespace names' do
it 'updates a user namespace within range' do
user2 = users.create(name: "Other user's full name", projects_limit: 10, username: 'also-not-null', email: '2')
user_namespace1 = namespaces.create(
id: 2,
owner_id: user.id,
name: "Should be the user's name",
path: user.username
)
user_namespace2 = namespaces.create(
id: 3,
owner_id: user2.id,
name: "Should also be the user's name",
path: user.username
)
described_class.new.perform(1, 5)
expect(user_namespace1.reload.name).to eq("The user's full name")
expect(user_namespace2.reload.name).to eq("Other user's full name")
end
it 'does not update namespaces out of range' do
user_namespace = namespaces.create(
id: 6,
owner_id: user.id,
name: "Should be the user's name",
path: user.username
)
expect { described_class.new.perform(1, 5) }
.not_to change { user_namespace.reload.name }
end
it 'does not update groups owned by the users' do
user_group = namespaces.create(
id: 2,
owner_id: user.id,
name: 'A group name',
path: 'the-path',
type: 'Group'
)
expect { described_class.new.perform(1, 5) }
.not_to change { user_group.reload.name }
end
end
context 'namespace route names' do
let(:routes) { table(:routes) }
let(:namespace) do
namespaces.create(
id: 2,
owner_id: user.id,
name: "Will be updated to the user's name",
path: user.username
)
end
it "updates the route name if it didn't match the namespace" do
route = routes.create(path: namespace.path, name: 'Incorrect name', source_type: 'Namespace', source_id: namespace.id)
described_class.new.perform(1, 5)
expect(route.reload.name).to eq("The user's full name")
end
it 'updates the route name if it was nil match the namespace' do
route = routes.create(path: namespace.path, name: nil, source_type: 'Namespace', source_id: namespace.id)
described_class.new.perform(1, 5)
expect(route.reload.name).to eq("The user's full name")
end
it "doesn't update group routes" do
route = routes.create(path: 'group-path', name: 'Group name', source_type: 'Group', source_id: namespace.id)
expect { described_class.new.perform(1, 5) }
.not_to change { route.reload.name }
end
it "doesn't touch routes for namespaces out of range" do
user_namespace = namespaces.create(
id: 6,
owner_id: user.id,
name: "Should be the user's name",
path: user.username
)
expect { described_class.new.perform(1, 5) }
.not_to change { user_namespace.reload.name }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, :migration, schema: 20190620112608 do
let(:namespaces) { table(:namespaces) }
let(:users) { table(:users) }
let(:routes) { table(:routes) }
let(:projects) { table(:projects) }
let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
let(:namespace) do
namespaces.create(
owner_id: user.id,
name: "Should eventually be the user's name",
path: user.username
)
end
let(:project) do
projects.create(namespace_id: namespace.id, name: 'Project Name')
end
it "updates the route for a project if it did not match the user's name" do
route = routes.create(
id: 1,
path: "#{user.username}/#{project.path}",
source_id: project.id,
source_type: 'Project',
name: 'Completely wrong'
)
described_class.new.perform(1, 5)
expect(route.reload.name).to eq("The user's full name / Project Name")
end
it 'updates the route for a project if the name was nil' do
route = routes.create(
id: 1,
path: "#{user.username}/#{project.path}",
source_id: project.id,
source_type: 'Project',
name: nil
)
described_class.new.perform(1, 5)
expect(route.reload.name).to eq("The user's full name / Project Name")
end
it 'does not update routes that were are out of the range' do
route = routes.create(
id: 6,
path: "#{user.username}/#{project.path}",
source_id: project.id,
source_type: 'Project',
name: 'Completely wrong'
)
expect { described_class.new.perform(1, 5) }
.not_to change { route.reload.name }
end
it 'does not update routes for projects in groups owned by the user' do
group = namespaces.create(
owner_id: user.id,
name: 'A group',
path: 'a-path',
type: ''
)
project = projects.create(namespace_id: group.id, name: 'Project Name')
route = routes.create(
id: 1,
path: "#{group.path}/#{project.path}",
source_id: project.id,
source_type: 'Project',
name: 'Completely wrong'
)
expect { described_class.new.perform(1, 5) }
.not_to change { route.reload.name }
end
it 'does not update routes for namespaces' do
route = routes.create(
id: 1,
path: namespace.path,
source_id: namespace.id,
source_type: 'Namespace',
name: 'Completely wrong'
)
expect { described_class.new.perform(1, 5) }
.not_to change { route.reload.name }
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