Merge branch 'da-handle-hashed-storage-repos-using-repo-import-task' into 'master'
Handle GitLab hashed storage repositories using the repo import task Closes #39870 See merge request gitlab-org/gitlab-ce!16027
This commit is contained in:
commit
034a21ba4d
18 changed files with 266 additions and 68 deletions
|
@ -34,6 +34,8 @@ module Storage
|
||||||
# So we basically we mute exceptions in next actions
|
# So we basically we mute exceptions in next actions
|
||||||
begin
|
begin
|
||||||
send_update_instructions
|
send_update_instructions
|
||||||
|
write_projects_repository_config
|
||||||
|
|
||||||
true
|
true
|
||||||
rescue
|
rescue
|
||||||
# Returning false does not rollback after_* transaction but gives
|
# Returning false does not rollback after_* transaction but gives
|
||||||
|
|
|
@ -268,4 +268,11 @@ class Namespace < ActiveRecord::Base
|
||||||
def namespace_previously_created_with_same_path?
|
def namespace_previously_created_with_same_path?
|
||||||
RedirectRoute.permanent.exists?(path: path)
|
RedirectRoute.permanent.exists?(path: path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write_projects_repository_config
|
||||||
|
all_projects.find_each do |project|
|
||||||
|
project.expires_full_path_cache # we need to clear cache to validate renames correctly
|
||||||
|
project.write_repository_config
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1420,6 +1420,8 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_rename_repo
|
def after_rename_repo
|
||||||
|
write_repository_config
|
||||||
|
|
||||||
path_before_change = previous_changes['path'].first
|
path_before_change = previous_changes['path'].first
|
||||||
|
|
||||||
# We need to check if project had been rolled out to move resource to hashed storage or not and decide
|
# We need to check if project had been rolled out to move resource to hashed storage or not and decide
|
||||||
|
@ -1432,6 +1434,16 @@ class Project < ActiveRecord::Base
|
||||||
Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
|
Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write_repository_config(gl_full_path: full_path)
|
||||||
|
# We'd need to keep track of project full path otherwise directory tree
|
||||||
|
# created with hashed storage enabled cannot be usefully imported using
|
||||||
|
# the import rake task.
|
||||||
|
repo.config['gitlab.fullpath'] = gl_full_path
|
||||||
|
rescue Gitlab::Git::Repository::NoRepository => e
|
||||||
|
Rails.logger.error("Error writing to .git/config for project #{full_path} (#{id}): #{e.message}.")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
def rename_repo_notify!
|
def rename_repo_notify!
|
||||||
send_move_instructions(full_path_was)
|
send_move_instructions(full_path_was)
|
||||||
expires_full_path_cache
|
expires_full_path_cache
|
||||||
|
|
|
@ -88,6 +88,7 @@ module Projects
|
||||||
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
|
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
|
||||||
|
|
||||||
unless @project.gitlab_project_import?
|
unless @project.gitlab_project_import?
|
||||||
|
@project.write_repository_config
|
||||||
@project.create_wiki unless skip_wiki?
|
@project.create_wiki unless skip_wiki?
|
||||||
create_services_from_active_templates(@project)
|
create_services_from_active_templates(@project)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@ module Projects
|
||||||
result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
|
result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
|
||||||
end
|
end
|
||||||
|
|
||||||
unless result
|
if result
|
||||||
|
project.write_repository_config
|
||||||
|
else
|
||||||
rollback_folder_move
|
rollback_folder_move
|
||||||
project.storage_version = nil
|
project.storage_version = nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,6 +75,8 @@ module Projects
|
||||||
project.old_path_with_namespace = @old_path
|
project.old_path_with_namespace = @old_path
|
||||||
project.expires_full_path_cache
|
project.expires_full_path_cache
|
||||||
|
|
||||||
|
write_repository_config(@new_path)
|
||||||
|
|
||||||
execute_system_hooks
|
execute_system_hooks
|
||||||
end
|
end
|
||||||
rescue Exception # rubocop:disable Lint/RescueException
|
rescue Exception # rubocop:disable Lint/RescueException
|
||||||
|
@ -98,6 +100,10 @@ module Projects
|
||||||
project.save!
|
project.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write_repository_config(full_path)
|
||||||
|
project.write_repository_config(gl_full_path: full_path)
|
||||||
|
end
|
||||||
|
|
||||||
def refresh_permissions
|
def refresh_permissions
|
||||||
# This ensures we only schedule 1 job for every user that has access to
|
# This ensures we only schedule 1 job for every user that has access to
|
||||||
# the namespaces.
|
# the namespaces.
|
||||||
|
@ -110,6 +116,7 @@ module Projects
|
||||||
def rollback_side_effects
|
def rollback_side_effects
|
||||||
rollback_folder_move
|
rollback_folder_move
|
||||||
update_namespace_and_visibility(@old_namespace)
|
update_namespace_and_visibility(@old_namespace)
|
||||||
|
write_repository_config(@old_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def rollback_folder_move
|
def rollback_folder_move
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Handle GitLab hashed storage repositories using the repo import task
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -14,7 +14,7 @@ module Gitlab
|
||||||
repos_to_import.each do |repo_path|
|
repos_to_import.each do |repo_path|
|
||||||
bare_repo = Gitlab::BareRepositoryImport::Repository.new(import_path, repo_path)
|
bare_repo = Gitlab::BareRepositoryImport::Repository.new(import_path, repo_path)
|
||||||
|
|
||||||
if bare_repo.hashed? || bare_repo.wiki?
|
unless bare_repo.processable?
|
||||||
log " * Skipping repo #{bare_repo.repo_path}".color(:yellow)
|
log " * Skipping repo #{bare_repo.repo_path}".color(:yellow)
|
||||||
|
|
||||||
next
|
next
|
||||||
|
@ -62,6 +62,8 @@ module Gitlab
|
||||||
if project.persisted? && mv_repo(project)
|
if project.persisted? && mv_repo(project)
|
||||||
log " * Created #{project.name} (#{project_full_path})".color(:green)
|
log " * Created #{project.name} (#{project_full_path})".color(:green)
|
||||||
|
|
||||||
|
project.write_repository_config
|
||||||
|
|
||||||
ProjectCacheWorker.perform_async(project.id)
|
ProjectCacheWorker.perform_async(project.id)
|
||||||
else
|
else
|
||||||
log " * Failed trying to create #{project.name} (#{project_full_path})".color(:red)
|
log " * Failed trying to create #{project.name} (#{project_full_path})".color(:red)
|
||||||
|
|
|
@ -6,39 +6,56 @@ module Gitlab
|
||||||
def initialize(root_path, repo_path)
|
def initialize(root_path, repo_path)
|
||||||
@root_path = root_path
|
@root_path = root_path
|
||||||
@repo_path = repo_path
|
@repo_path = repo_path
|
||||||
|
|
||||||
@root_path << '/' unless root_path.ends_with?('/')
|
@root_path << '/' unless root_path.ends_with?('/')
|
||||||
|
|
||||||
|
full_path =
|
||||||
|
if hashed? && !wiki?
|
||||||
|
repository.config.get('gitlab.fullpath')
|
||||||
|
else
|
||||||
|
repo_relative_path
|
||||||
|
end
|
||||||
|
|
||||||
# Split path into 'all/the/namespaces' and 'project_name'
|
# Split path into 'all/the/namespaces' and 'project_name'
|
||||||
@group_path, _, @project_name = repo_relative_path.rpartition('/')
|
@group_path, _, @project_name = full_path.to_s.rpartition('/')
|
||||||
end
|
end
|
||||||
|
|
||||||
def wiki_exists?
|
def wiki_exists?
|
||||||
File.exist?(wiki_path)
|
File.exist?(wiki_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def wiki?
|
|
||||||
@wiki ||= repo_path.end_with?('.wiki.git')
|
|
||||||
end
|
|
||||||
|
|
||||||
def wiki_path
|
def wiki_path
|
||||||
@wiki_path ||= repo_path.sub(/\.git$/, '.wiki.git')
|
@wiki_path ||= repo_path.sub(/\.git$/, '.wiki.git')
|
||||||
end
|
end
|
||||||
|
|
||||||
def hashed?
|
|
||||||
@hashed ||= group_path.start_with?('@hashed')
|
|
||||||
end
|
|
||||||
|
|
||||||
def project_full_path
|
def project_full_path
|
||||||
@project_full_path ||= "#{group_path}/#{project_name}"
|
@project_full_path ||= "#{group_path}/#{project_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def processable?
|
||||||
|
return false if wiki?
|
||||||
|
return false if hashed? && (group_path.blank? || project_name.blank?)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def wiki?
|
||||||
|
@wiki ||= repo_path.end_with?('.wiki.git')
|
||||||
|
end
|
||||||
|
|
||||||
|
def hashed?
|
||||||
|
@hashed ||= repo_relative_path.include?('@hashed')
|
||||||
|
end
|
||||||
|
|
||||||
def repo_relative_path
|
def repo_relative_path
|
||||||
# Remove root path and `.git` at the end
|
# Remove root path and `.git` at the end
|
||||||
repo_path[@root_path.size...-4]
|
repo_path[@root_path.size...-4]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def repository
|
||||||
|
@repository ||= Rugged::Repository.new(repo_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,7 +75,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates the Git repo in disk' do
|
it 'creates the Git repo in disk' do
|
||||||
FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
|
create_bare_repository("#{project_path}.git")
|
||||||
|
|
||||||
importer.create_project_if_needed
|
importer.create_project_if_needed
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates the Git repo in disk' do
|
it 'creates the Git repo in disk' do
|
||||||
FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
|
create_bare_repository("#{project_path}.git")
|
||||||
|
|
||||||
importer.create_project_if_needed
|
importer.create_project_if_needed
|
||||||
|
|
||||||
|
@ -165,8 +165,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
|
||||||
it_behaves_like 'importing a repository'
|
it_behaves_like 'importing a repository'
|
||||||
|
|
||||||
it 'creates the Wiki git repo in disk' do
|
it 'creates the Wiki git repo in disk' do
|
||||||
FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
|
create_bare_repository("#{project_path}.git")
|
||||||
FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.wiki.git"))
|
create_bare_repository("#{project_path}.wiki.git")
|
||||||
|
|
||||||
expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true,
|
expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true,
|
||||||
import_type: 'bare_repository')).and_call_original
|
import_type: 'bare_repository')).and_call_original
|
||||||
|
@ -192,4 +192,9 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_bare_repository(project_path)
|
||||||
|
repo_path = File.join(base_dir, project_path)
|
||||||
|
Gitlab::Git::Repository.create(repo_path, bare: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,58 +1,122 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe ::Gitlab::BareRepositoryImport::Repository do
|
describe ::Gitlab::BareRepositoryImport::Repository do
|
||||||
let(:project_repo_path) { described_class.new('/full/path/', '/full/path/to/repo.git') }
|
context 'legacy storage' do
|
||||||
|
subject { described_class.new('/full/path/', '/full/path/to/repo.git') }
|
||||||
|
|
||||||
it 'stores the repo path' do
|
it 'stores the repo path' do
|
||||||
expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git')
|
expect(subject.repo_path).to eq('/full/path/to/repo.git')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'stores the group path' do
|
it 'stores the group path' do
|
||||||
expect(project_repo_path.group_path).to eq('to')
|
expect(subject.group_path).to eq('to')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'stores the project name' do
|
it 'stores the project name' do
|
||||||
expect(project_repo_path.project_name).to eq('repo')
|
expect(subject.project_name).to eq('repo')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'stores the wiki path' do
|
it 'stores the wiki path' do
|
||||||
expect(project_repo_path.wiki_path).to eq('/full/path/to/repo.wiki.git')
|
expect(subject.wiki_path).to eq('/full/path/to/repo.wiki.git')
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wiki?' do
|
describe '#processable?' do
|
||||||
it 'returns true if it is a wiki' do
|
it 'returns false if it is a wiki' do
|
||||||
wiki_path = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git')
|
subject = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git')
|
||||||
|
|
||||||
expect(wiki_path.wiki?).to eq(true)
|
expect(subject).not_to be_processable
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if it is not a wiki' do
|
it 'returns true if group path is missing' do
|
||||||
expect(project_repo_path.wiki?).to eq(false)
|
subject = described_class.new('/full/path/', '/full/path/repo.git')
|
||||||
end
|
|
||||||
|
expect(subject).to be_processable
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#hashed?' do
|
it 'returns true when group path and project name are present' do
|
||||||
it 'returns true if it is a hashed folder' do
|
expect(subject).to be_processable
|
||||||
path = described_class.new('/full/path/', '/full/path/@hashed/my.repo.git')
|
|
||||||
|
|
||||||
expect(path.hashed?).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false if it is not a hashed folder' do
|
|
||||||
expect(project_repo_path.hashed?).to eq(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#project_full_path' do
|
describe '#project_full_path' do
|
||||||
it 'returns the project full path' do
|
it 'returns the project full path with trailing slash in the root path' do
|
||||||
expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git')
|
expect(subject.project_full_path).to eq('to/repo')
|
||||||
expect(project_repo_path.project_full_path).to eq('to/repo')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'with no trailing slash in the root path' do
|
it 'returns the project full path with no trailing slash in the root path' do
|
||||||
repo_path = described_class.new('/full/path', '/full/path/to/repo.git')
|
subject = described_class.new('/full/path', '/full/path/to/repo.git')
|
||||||
|
|
||||||
expect(repo_path.project_full_path).to eq('to/repo')
|
expect(subject.project_full_path).to eq('to/repo')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'hashed storage' do
|
||||||
|
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||||
|
let(:repository_storage) { 'default' }
|
||||||
|
let(:root_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
|
||||||
|
let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }
|
||||||
|
let(:hashed_path) { "@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" }
|
||||||
|
let(:repo_path) { File.join(root_path, "#{hashed_path}.git") }
|
||||||
|
let(:wiki_path) { File.join(root_path, "#{hashed_path}.wiki.git") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
gitlab_shell.add_repository(repository_storage, hashed_path)
|
||||||
|
repository = Rugged::Repository.new(repo_path)
|
||||||
|
repository.config['gitlab.fullpath'] = 'to/repo'
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
gitlab_shell.remove_repository(root_path, hashed_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(root_path, repo_path) }
|
||||||
|
|
||||||
|
it 'stores the repo path' do
|
||||||
|
expect(subject.repo_path).to eq(repo_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'stores the wiki path' do
|
||||||
|
expect(subject.wiki_path).to eq(wiki_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reads the group path from .git/config' do
|
||||||
|
expect(subject.group_path).to eq('to')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reads the project name from .git/config' do
|
||||||
|
expect(subject.project_name).to eq('repo')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#processable?' do
|
||||||
|
it 'returns false if it is a wiki' do
|
||||||
|
subject = described_class.new(root_path, wiki_path)
|
||||||
|
|
||||||
|
expect(subject).not_to be_processable
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false when group and project name are missing' do
|
||||||
|
repository = Rugged::Repository.new(repo_path)
|
||||||
|
repository.config.delete('gitlab.fullpath')
|
||||||
|
|
||||||
|
expect(subject).not_to be_processable
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true when group path and project name are present' do
|
||||||
|
expect(subject).to be_processable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#project_full_path' do
|
||||||
|
it 'returns the project full path with trailing slash in the root path' do
|
||||||
|
expect(subject.project_full_path).to eq('to/repo')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the project full path with no trailing slash in the root path' do
|
||||||
|
subject = described_class.new(root_path[0...-1], repo_path)
|
||||||
|
|
||||||
|
expect(subject.project_full_path).to eq('to/repo')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -203,7 +203,7 @@ describe Namespace do
|
||||||
context 'with subgroups' do
|
context 'with subgroups' do
|
||||||
let(:parent) { create(:group, name: 'parent', path: 'parent') }
|
let(:parent) { create(:group, name: 'parent', path: 'parent') }
|
||||||
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
|
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
|
||||||
let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child) }
|
let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child, skip_disk_validation: true) }
|
||||||
let(:uploads_dir) { File.join(CarrierWave.root, FileUploader.base_dir) }
|
let(:uploads_dir) { File.join(CarrierWave.root, FileUploader.base_dir) }
|
||||||
let(:pages_dir) { File.join(TestEnv.pages_path) }
|
let(:pages_dir) { File.join(TestEnv.pages_path) }
|
||||||
|
|
||||||
|
@ -240,6 +240,20 @@ describe Namespace do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates project full path in .git/config for each project inside namespace' do
|
||||||
|
parent = create(:group, name: 'mygroup', path: 'mygroup')
|
||||||
|
subgroup = create(:group, name: 'mysubgroup', path: 'mysubgroup', parent: parent)
|
||||||
|
project_in_parent_group = create(:project, :repository, namespace: parent, name: 'foo1')
|
||||||
|
hashed_project_in_subgroup = create(:project, :repository, :hashed, namespace: subgroup, name: 'foo2')
|
||||||
|
legacy_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo3')
|
||||||
|
|
||||||
|
parent.update(path: 'mygroup_new')
|
||||||
|
|
||||||
|
expect(project_in_parent_group.repo.config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
|
||||||
|
expect(hashed_project_in_subgroup.repo.config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
|
||||||
|
expect(legacy_project_in_subgroup.repo.config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#rm_dir', 'callback' do
|
describe '#rm_dir', 'callback' do
|
||||||
|
|
|
@ -2626,6 +2626,14 @@ describe Project do
|
||||||
project.rename_repo
|
project.rename_repo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates project full path in .git/config' do
|
||||||
|
allow(project_storage).to receive(:rename_repo).and_return(true)
|
||||||
|
|
||||||
|
project.rename_repo
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq(project.full_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#pages_path' do
|
describe '#pages_path' do
|
||||||
|
@ -2668,14 +2676,12 @@ describe Project do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'hashed storage' do
|
context 'hashed storage' do
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository, skip_disk_validation: true) }
|
||||||
let(:gitlab_shell) { Gitlab::Shell.new }
|
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||||
let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }
|
let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_application_setting(hashed_storage_enabled: true)
|
stub_application_setting(hashed_storage_enabled: true)
|
||||||
allow(Digest::SHA2).to receive(:hexdigest) { hash }
|
|
||||||
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#legacy_storage?' do
|
describe '#legacy_storage?' do
|
||||||
|
@ -2698,13 +2704,13 @@ describe Project do
|
||||||
|
|
||||||
describe '#base_dir' do
|
describe '#base_dir' do
|
||||||
it 'returns base_dir based on hash of project id' do
|
it 'returns base_dir based on hash of project id' do
|
||||||
expect(project.base_dir).to eq('@hashed/6b/86')
|
expect(project.base_dir).to eq("@hashed/#{hash[0..1]}/#{hash[2..3]}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#disk_path' do
|
describe '#disk_path' do
|
||||||
it 'returns disk_path based on hash of project id' do
|
it 'returns disk_path based on hash of project id' do
|
||||||
hashed_path = '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b'
|
hashed_path = "@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}"
|
||||||
|
|
||||||
expect(project.disk_path).to eq(hashed_path)
|
expect(project.disk_path).to eq(hashed_path)
|
||||||
end
|
end
|
||||||
|
@ -2712,7 +2718,9 @@ describe Project do
|
||||||
|
|
||||||
describe '#ensure_storage_path_exists' do
|
describe '#ensure_storage_path_exists' do
|
||||||
it 'delegates to gitlab_shell to ensure namespace is created' do
|
it 'delegates to gitlab_shell to ensure namespace is created' do
|
||||||
expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, '@hashed/6b/86')
|
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
|
||||||
|
|
||||||
|
expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, "@hashed/#{hash[0..1]}/#{hash[2..3]}")
|
||||||
|
|
||||||
project.ensure_storage_path_exists
|
project.ensure_storage_path_exists
|
||||||
end
|
end
|
||||||
|
@ -2772,7 +2780,7 @@ describe Project do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not rolled out' do
|
context 'when not rolled out' do
|
||||||
let(:project) { create(:project, :repository, storage_version: 1) }
|
let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
|
||||||
|
|
||||||
it 'moves pages folder to new location' do
|
it 'moves pages folder to new location' do
|
||||||
expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
|
expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
|
||||||
|
@ -2781,6 +2789,12 @@ describe Project do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates project full path in .git/config' do
|
||||||
|
project.rename_repo
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq(project.full_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#pages_path' do
|
describe '#pages_path' do
|
||||||
|
@ -3141,4 +3155,26 @@ describe Project do
|
||||||
it { is_expected.to eq(platform_kubernetes) }
|
it { is_expected.to eq(platform_kubernetes) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#write_repository_config' do
|
||||||
|
set(:project) { create(:project, :repository) }
|
||||||
|
|
||||||
|
it 'writes full path in .git/config when key is missing' do
|
||||||
|
project.write_repository_config
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates full path in .git/config when key is present' do
|
||||||
|
project.write_repository_config(gl_full_path: 'old/path')
|
||||||
|
|
||||||
|
expect { project.write_repository_config }.to change { project.repo.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not raise an error with an empty repository' do
|
||||||
|
project = create(:project_empty_repo)
|
||||||
|
|
||||||
|
expect { project.write_repository_config }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -252,6 +252,12 @@ describe Projects::CreateService, '#execute' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'writes project full path to .git/config' do
|
||||||
|
project = create_project(user, opts)
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
|
||||||
|
end
|
||||||
|
|
||||||
def create_project(user, opts)
|
def create_project(user, opts)
|
||||||
Projects::CreateService.new(user, opts).execute
|
Projects::CreateService.new(user, opts).execute
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe Projects::HashedStorage::MigrateRepositoryService do
|
describe Projects::HashedStorage::MigrateRepositoryService do
|
||||||
let(:gitlab_shell) { Gitlab::Shell.new }
|
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||||
let(:project) { create(:project, :empty_repo, :wiki_repo) }
|
let(:project) { create(:project, :repository, :wiki_repo) }
|
||||||
let(:service) { described_class.new(project) }
|
let(:service) { described_class.new(project) }
|
||||||
let(:legacy_storage) { Storage::LegacyProject.new(project) }
|
let(:legacy_storage) { Storage::LegacyProject.new(project) }
|
||||||
let(:hashed_storage) { Storage::HashedProject.new(project) }
|
let(:hashed_storage) { Storage::HashedProject.new(project) }
|
||||||
|
@ -33,6 +33,12 @@ describe Projects::HashedStorage::MigrateRepositoryService do
|
||||||
|
|
||||||
service.execute
|
service.execute
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'writes project full path to .git/config' do
|
||||||
|
service.execute
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when one move fails' do
|
context 'when one move fails' do
|
||||||
|
|
|
@ -54,6 +54,12 @@ describe Projects::TransferService do
|
||||||
expect(project.disk_path).not_to eq(old_path)
|
expect(project.disk_path).not_to eq(old_path)
|
||||||
expect(project.disk_path).to start_with(group.path)
|
expect(project.disk_path).to start_with(group.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates project full path in .git/config' do
|
||||||
|
transfer_project(project, user, group)
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when transfer fails' do
|
context 'when transfer fails' do
|
||||||
|
@ -86,6 +92,12 @@ describe Projects::TransferService do
|
||||||
expect(original_path).to eq current_path
|
expect(original_path).to eq current_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'rolls back project full path in .git/config' do
|
||||||
|
attempt_project_transfer
|
||||||
|
|
||||||
|
expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
|
||||||
|
end
|
||||||
|
|
||||||
it "doesn't send move notifications" do
|
it "doesn't send move notifications" do
|
||||||
expect_any_instance_of(NotificationService).not_to receive(:project_was_moved)
|
expect_any_instance_of(NotificationService).not_to receive(:project_was_moved)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ describe Projects::UpdateService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'When project visibility is higher than parent group' do
|
context 'when project visibility is higher than parent group' do
|
||||||
let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
|
let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
|
|
@ -215,7 +215,7 @@ module TestEnv
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy_repo(project, bare_repo:, refs:)
|
def copy_repo(project, bare_repo:, refs:)
|
||||||
target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git")
|
target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.disk_path}.git")
|
||||||
FileUtils.mkdir_p(target_repo_path)
|
FileUtils.mkdir_p(target_repo_path)
|
||||||
FileUtils.cp_r("#{File.expand_path(bare_repo)}/.", target_repo_path)
|
FileUtils.cp_r("#{File.expand_path(bare_repo)}/.", target_repo_path)
|
||||||
FileUtils.chmod_R 0755, target_repo_path
|
FileUtils.chmod_R 0755, target_repo_path
|
||||||
|
|
Loading…
Reference in a new issue