summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@vanlanduyt.co>2019-06-28 20:00:52 +0200
committerBob Van Landuyt <bob@vanlanduyt.co>2019-07-09 18:09:45 +0200
commit82e6ed310b1bb5e7faf44742defaf65b74926195 (patch)
treeb5b0e7fc1dbc539e4c36064a4e3e9da0e0e6a3f5
parentf138acb9866fa86f16e1db14c23e48c2e9f2b38b (diff)
downloadgitlab-ce-82e6ed310b1bb5e7faf44742defaf65b74926195.tar.gz
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
-rw-r--r--db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb48
-rw-r--r--lib/gitlab/background_migration/fix_user_namespace_names.rb68
-rw-r--r--lib/gitlab/background_migration/fix_user_project_route_names.rb38
-rw-r--r--spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb104
-rw-r--r--spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb98
5 files changed, 356 insertions, 0 deletions
diff --git a/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb b/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb
new file mode 100644
index 00000000000..8fa7068b957
--- /dev/null
+++ b/db/post_migrate/20190628191740_schedule_fixing_names_of_user_namespaces.rb
@@ -0,0 +1,48 @@
+# 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
diff --git a/lib/gitlab/background_migration/fix_user_namespace_names.rb b/lib/gitlab/background_migration/fix_user_namespace_names.rb
new file mode 100644
index 00000000000..1a207121be0
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_user_namespace_names.rb
@@ -0,0 +1,68 @@
+# 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
diff --git a/lib/gitlab/background_migration/fix_user_project_route_names.rb b/lib/gitlab/background_migration/fix_user_project_route_names.rb
new file mode 100644
index 00000000000..b84ff32e712
--- /dev/null
+++ b/lib/gitlab/background_migration/fix_user_project_route_names.rb
@@ -0,0 +1,38 @@
+# 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
diff --git a/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb
new file mode 100644
index 00000000000..5938ecca459
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb
@@ -0,0 +1,104 @@
+# 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
diff --git a/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb
new file mode 100644
index 00000000000..d1d6d8411d1
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb
@@ -0,0 +1,98 @@
+# 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