summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelogs/unreleased/tc-backfill-full-path-config.yml5
-rw-r--r--db/post_migrate/20181010133639_backfill_store_project_full_path_in_repo.rb136
-rw-r--r--spec/migrations/backfill_store_project_full_path_in_repo_spec.rb70
3 files changed, 211 insertions, 0 deletions
diff --git a/changelogs/unreleased/tc-backfill-full-path-config.yml b/changelogs/unreleased/tc-backfill-full-path-config.yml
new file mode 100644
index 00000000000..4f06284d0e3
--- /dev/null
+++ b/changelogs/unreleased/tc-backfill-full-path-config.yml
@@ -0,0 +1,5 @@
+---
+title: Migration to write fullpath in all repository configs
+merge_request: 22322
+author:
+type: other
diff --git a/db/post_migrate/20181010133639_backfill_store_project_full_path_in_repo.rb b/db/post_migrate/20181010133639_backfill_store_project_full_path_in_repo.rb
new file mode 100644
index 00000000000..1e2e4527d2b
--- /dev/null
+++ b/db/post_migrate/20181010133639_backfill_store_project_full_path_in_repo.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+class BackfillStoreProjectFullPathInRepo < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ class Repository
+ attr_reader :storage
+
+ def initialize(storage, relative_path)
+ @storage = storage
+ @relative_path = relative_path
+ end
+
+ def gitaly_repository
+ Gitaly::Repository.new(storage_name: @storage, relative_path: @relative_path)
+ end
+ end
+
+ module Storage
+ class HashedProject
+ attr_accessor :project
+
+ ROOT_PATH_PREFIX = '@hashed'.freeze
+
+ def initialize(project)
+ @project = project
+ end
+
+ def disk_path
+ "#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}/#{disk_hash}"
+ end
+
+ def disk_hash
+ @disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
+ end
+ end
+
+ class LegacyProject
+ attr_accessor :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def disk_path
+ project.full_path
+ end
+ end
+ end
+
+ module Routable
+ extend ActiveSupport::Concern
+
+ def full_path
+ @full_path ||= build_full_path
+ end
+
+ def build_full_path
+ if parent && path
+ parent.full_path + '/' + path
+ else
+ path
+ end
+ end
+ end
+
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+
+ include Routable
+
+ belongs_to :parent, class_name: "Namespace"
+ end
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ include Routable
+ include EachBatch
+
+ FULLPATH_CONFIG_KEY = 'gitlab.fullpath'
+
+ belongs_to :namespace
+ delegate :disk_path, to: :storage
+ alias_method :parent, :namespace
+
+ def add_fullpath_config
+ entries = { FULLPATH_CONFIG_KEY => full_path }
+
+ repository_service.set_config(entries)
+ end
+
+ def remove_fullpath_config
+ repository_service.delete_config([FULLPATH_CONFIG_KEY])
+ end
+
+ def storage
+ @storage ||=
+ if hashed_storage?
+ Storage::HashedProject.new(self)
+ else
+ Storage::LegacyProject.new(self)
+ end
+ end
+
+ def hashed_storage?
+ self.storage_version && self.storage_version >= 1
+ end
+
+ def repository
+ @repository ||= Repository.new(repository_storage, disk_path + '.git')
+ end
+
+ def repository_service
+ @repository_service ||= Gitlab::GitalyClient::RepositoryService.new(repository)
+ end
+ end
+
+ def up
+ Project.each_batch do |batch|
+ batch.each do |project|
+ project.add_fullpath_config
+ end
+ end
+ end
+
+ def down
+ Project.each_batch do |batch|
+ batch.each do |project|
+ project.remove_fullpath_config
+ end
+ end
+ end
+end
diff --git a/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb b/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb
new file mode 100644
index 00000000000..f2651f8a02f
--- /dev/null
+++ b/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require Rails.root.join('db', 'post_migrate', '20181010133639_backfill_store_project_full_path_in_repo.rb')
+
+describe BackfillStoreProjectFullPathInRepo, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:group) { namespaces.create!(name: 'foo', path: 'foo') }
+ let(:subgroup) { namespaces.create!(name: 'bar', path: 'bar', parent_id: group.id) }
+
+ subject(:migration) { described_class.new }
+
+ describe '#up' do
+ shared_examples_for 'writes the full path to git config' do
+ it 'writes the git config' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService)
+ .to receive(:set_config).with('gitlab.fullpath' => expected_path)
+
+ migration.up
+ end
+
+ context 'legacy storage' do
+ it 'finds the repository at the correct location' do
+ Project.find(project.id).create_repository
+
+ expect { migration.up }.not_to raise_error
+ end
+ end
+
+ context 'hashed storage' do
+ it 'finds the repository at the correct location' do
+ project.update_attribute(:storage_version, 1)
+
+ Project.find(project.id).create_repository
+
+ expect { migration.up }.not_to raise_error
+ end
+ end
+ end
+
+ context 'project in group' do
+ let!(:project) { projects.create!(namespace_id: group.id, name: 'baz', path: 'baz') }
+ let(:expected_path) { 'foo/baz' }
+
+ it_behaves_like 'writes the full path to git config'
+ end
+
+ context 'project in subgroup' do
+ let!(:project) { projects.create!(namespace_id: subgroup.id, name: 'baz', path: 'baz') }
+ let(:expected_path) { 'foo/bar/baz' }
+
+ it_behaves_like 'writes the full path to git config'
+ end
+ end
+
+ describe '#down' do
+ context 'project in group' do
+ let!(:project) { projects.create!(namespace_id: group.id, name: 'baz', path: 'baz') }
+
+ it 'deletes the gitlab full config value' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService)
+ .to receive(:delete_config).with(['gitlab.fullpath'])
+
+ migration.down
+ end
+ end
+ end
+end