Commit 1517db42 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch...

Merge branch 'ee-46940-hashed-storage-extend-enable-hashed-storage-for-all-new-projects-to-for-all-new-and-renamed-projects' into 'master'

EE version of https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19747

See merge request gitlab-org/gitlab-ee!6710
parents f52093d0 68869469
...@@ -1586,47 +1586,33 @@ class Project < ActiveRecord::Base ...@@ -1586,47 +1586,33 @@ class Project < ActiveRecord::Base
end end
def rename_repo def rename_repo
new_full_path = build_full_path path_before = previous_changes['path'].first
full_path_before = full_path_was
full_path_after = build_full_path
Rails.logger.error "Attempting to rename #{full_path_was} -> #{new_full_path}" Gitlab::AppLogger.info("Attempting to rename #{full_path_was} -> #{full_path_after}")
if has_container_registry_tags? if has_container_registry_tags?
Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!" Gitlab::AppLogger.info("Project #{full_path_was} cannot be renamed because container registry tags are present!")
# we currently doesn't support renaming repository if it contains images in container registry # we currently don't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry') raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end end
expire_caches_before_rename(full_path_was) expire_caches_before_rename(full_path_before)
if storage.rename_repo if rename_or_migrate_repository!
Gitlab::AppLogger.info "Project was renamed: #{full_path_was} -> #{new_full_path}" Gitlab::AppLogger.info("Project was renamed: #{full_path_before} -> #{full_path_after}")
rename_repo_notify! after_rename_repository(full_path_before, path_before)
after_rename_repo
else else
Rails.logger.error "Repository could not be renamed: #{full_path_was} -> #{new_full_path}" Gitlab::AppLogger.info("Repository could not be renamed: #{full_path_before} -> #{full_path_after}")
# if we cannot move namespace directory we should rollback # if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs # db changes in order to prevent out of sync between db and fs
raise StandardError.new('repository cannot be renamed') raise StandardError.new('Repository cannot be renamed')
end end
end end
def after_rename_repo
write_repository_config
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
# if we need execute any take action or no-op.
unless hashed_storage?(:attachments)
Gitlab::UploadsTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
end
Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
end
def write_repository_config(gl_full_path: full_path) def write_repository_config(gl_full_path: full_path)
# We'd need to keep track of project full path otherwise directory tree # We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using # created with hashed storage enabled cannot be usefully imported using
...@@ -1637,17 +1623,6 @@ class Project < ActiveRecord::Base ...@@ -1637,17 +1623,6 @@ class Project < ActiveRecord::Base
nil nil
end end
def rename_repo_notify!
# When we import a project overwriting the original project, there
# is a move operation. In that case we don't want to send the instructions.
send_move_instructions(full_path_was) unless import_started?
self.old_path_with_namespace = full_path_was
SystemHooksService.new.execute_hooks_for(self, :rename)
reload_repository!
end
def after_import def after_import
repository.after_import repository.after_import
wiki.repository.after_import wiki.repository.after_import
...@@ -2068,6 +2043,39 @@ class Project < ActiveRecord::Base ...@@ -2068,6 +2043,39 @@ class Project < ActiveRecord::Base
private private
def rename_or_migrate_repository!
if Gitlab::CurrentSettings.hashed_storage_enabled? && storage_version != LATEST_STORAGE_VERSION
::Projects::HashedStorageMigrationService.new(self, full_path_was).execute
else
storage.rename_repo
end
end
def after_rename_repository(full_path_before, path_before)
execute_rename_repository_hooks!(full_path_before)
write_repository_config
# We need to check if project had been rolled out to move resource to hashed storage or not and decide
# if we need execute any take action or no-op.
unless hashed_storage?(:attachments)
Gitlab::UploadsTransfer.new.rename_project(path_before, self.path, namespace.full_path)
end
Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path)
end
def execute_rename_repository_hooks!(full_path_before)
# When we import a project overwriting the original project, there
# is a move operation. In that case we don't want to send the instructions.
send_move_instructions(full_path_before) unless import_started?
self.old_path_with_namespace = full_path_before
SystemHooksService.new.execute_hooks_for(self, :rename)
reload_repository!
end
def storage def storage
@storage ||= @storage ||=
if hashed_storage?(:repository) if hashed_storage?(:repository)
......
...@@ -5,20 +5,22 @@ module Projects ...@@ -5,20 +5,22 @@ module Projects
AttachmentMigrationError = Class.new(StandardError) AttachmentMigrationError = Class.new(StandardError)
class MigrateAttachmentsService < BaseService class MigrateAttachmentsService < BaseService
attr_reader :logger, :old_path, :new_path attr_reader :logger, :old_disk_path, :new_disk_path
prepend ::EE::Projects::HashedStorage::MigrateAttachmentsService prepend ::EE::Projects::HashedStorage::MigrateAttachmentsService
def initialize(project, logger = nil) def initialize(project, old_disk_path, logger: nil)
@project = project @project = project
@logger = logger || Rails.logger @logger = logger || Rails.logger
@old_disk_path = old_disk_path
@new_disk_path = project.disk_path
end end
def execute def execute
@old_path = project.full_path
@new_path = project.disk_path
origin = FileUploader.absolute_base_dir(project) origin = FileUploader.absolute_base_dir(project)
# It's possible that old_disk_path does not match project.disk_path. For example, that happens when we rename a project
origin.sub!(/#{Regexp.escape(project.full_path)}\z/, old_disk_path)
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:attachments] project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:attachments]
target = FileUploader.absolute_base_dir(project) target = FileUploader.absolute_base_dir(project)
...@@ -34,22 +36,22 @@ module Projects ...@@ -34,22 +36,22 @@ module Projects
private private
def move_folder!(old_path, new_path) def move_folder!(old_disk_path, new_disk_path)
unless File.directory?(old_path) unless File.directory?(old_disk_path)
logger.info("Skipped attachments migration from '#{old_path}' to '#{new_path}', source path doesn't exist or is not a directory (PROJECT_ID=#{project.id})") logger.info("Skipped attachments migration from '#{old_disk_path}' to '#{new_disk_path}', source path doesn't exist or is not a directory (PROJECT_ID=#{project.id})")
return return
end end
if File.exist?(new_path) if File.exist?(new_disk_path)
logger.error("Cannot migrate attachments from '#{old_path}' to '#{new_path}', target path already exist (PROJECT_ID=#{project.id})") logger.error("Cannot migrate attachments from '#{old_disk_path}' to '#{new_disk_path}', target path already exist (PROJECT_ID=#{project.id})")
raise AttachmentMigrationError, "Target path '#{new_path}' already exist" raise AttachmentMigrationError, "Target path '#{new_disk_path}' already exist"
end end
# Create hashed storage base path folder # Create hashed storage base path folder
FileUtils.mkdir_p(File.dirname(new_path)) FileUtils.mkdir_p(File.dirname(new_disk_path))
FileUtils.mv(old_path, new_path) FileUtils.mv(old_disk_path, new_disk_path)
logger.info("Migrated project attachments from '#{old_path}' to '#{new_path}' (PROJECT_ID=#{project.id})") logger.info("Migrated project attachments from '#{old_disk_path}' to '#{new_disk_path}' (PROJECT_ID=#{project.id})")
true true
end end
......
...@@ -7,28 +7,27 @@ module Projects ...@@ -7,28 +7,27 @@ module Projects
prepend ::EE::Projects::HashedStorage::MigrateRepositoryService prepend ::EE::Projects::HashedStorage::MigrateRepositoryService
attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger, :move_wiki
def initialize(project, logger = nil) def initialize(project, old_disk_path, logger: nil)
@project = project @project = project
@logger = logger || Rails.logger @logger = logger || Rails.logger
@old_disk_path = old_disk_path
@old_wiki_disk_path = "#{old_disk_path}.wiki"
@move_wiki = has_wiki?
end end
def execute def execute
@old_disk_path = project.disk_path
has_wiki = project.wiki.repository_exists?
@old_storage_version = project.storage_version @old_storage_version = project.storage_version
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository] project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
project.ensure_storage_path_exists project.ensure_storage_path_exists
@new_disk_path = project.disk_path @new_disk_path = project.disk_path
result = move_repository(@old_disk_path, @new_disk_path) result = move_repository(old_disk_path, new_disk_path)
if has_wiki if move_wiki
@old_wiki_disk_path = "#{@old_disk_path}.wiki" result &&= move_repository("#{old_wiki_disk_path}", "#{new_disk_path}.wiki")
result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
end end
if result if result
...@@ -50,6 +49,10 @@ module Projects ...@@ -50,6 +49,10 @@ module Projects
private private
def has_wiki?
gitlab_shell.exists?(project.repository_storage, "#{old_wiki_disk_path}.git")
end
def move_repository(from_name, to_name) def move_repository(from_name, to_name)
from_exists = gitlab_shell.exists?(project.repository_storage, "#{from_name}.git") from_exists = gitlab_shell.exists?(project.repository_storage, "#{from_name}.git")
to_exists = gitlab_shell.exists?(project.repository_storage, "#{to_name}.git") to_exists = gitlab_shell.exists?(project.repository_storage, "#{to_name}.git")
...@@ -68,8 +71,8 @@ module Projects ...@@ -68,8 +71,8 @@ module Projects
end end
def rollback_folder_move def rollback_folder_move
move_repository(@new_disk_path, @old_disk_path) move_repository(new_disk_path, old_disk_path)
move_repository("#{@new_disk_path}.wiki", "#{@old_disk_path}.wiki") move_repository("#{new_disk_path}.wiki", old_wiki_disk_path)
end end
end end
end end
......
...@@ -2,23 +2,26 @@ ...@@ -2,23 +2,26 @@
module Projects module Projects
class HashedStorageMigrationService < BaseService class HashedStorageMigrationService < BaseService
attr_reader :logger attr_reader :logger, :old_disk_path
def initialize(project, logger = nil) def initialize(project, old_disk_path, logger: nil)
@project = project @project = project
@old_disk_path = old_disk_path
@logger = logger || Rails.logger @logger = logger || Rails.logger
end end
def execute def execute
# Migrate repository from Legacy to Hashed Storage # Migrate repository from Legacy to Hashed Storage
unless project.hashed_storage?(:repository) unless project.hashed_storage?(:repository)
return unless HashedStorage::MigrateRepositoryService.new(project, logger).execute return unless HashedStorage::MigrateRepositoryService.new(project, old_disk_path, logger: logger).execute
end end
# Migrate attachments from Legacy to Hashed Storage # Migrate attachments from Legacy to Hashed Storage
unless project.hashed_storage?(:attachments) unless project.hashed_storage?(:attachments)
HashedStorage::MigrateAttachmentsService.new(project, logger).execute HashedStorage::MigrateAttachmentsService.new(project, old_disk_path, logger: logger).execute
end end
true
end end
end end
end end
...@@ -6,33 +6,27 @@ module Projects ...@@ -6,33 +6,27 @@ module Projects
prepend ::EE::Projects::UpdateService prepend ::EE::Projects::UpdateService
def execute ValidationError = Class.new(StandardError)
unless valid_visibility_level_change?(project, params[:visibility_level])
return error('New visibility level not allowed!')
end
if renaming_project_with_container_registry_tags?
return error('Cannot rename project because it contains container registry tags!')
end
if changing_default_branch? def execute
return error("Could not set the default branch") unless project.change_head(params[:default_branch]) validate!
end
ensure_wiki_exists if enabling_wiki? ensure_wiki_exists if enabling_wiki?
yield if block_given? yield if block_given?
# If the block added errors, don't try to save the project # If the block added errors, don't try to save the project
return validation_failed! if project.errors.any? return update_failed! if project.errors.any?
if project.update(params.except(:default_branch)) if project.update(params.except(:default_branch))
after_update after_update
success success
else else
validation_failed! update_failed!
end end
rescue ValidationError => e
error(e.message)
end end
def run_auto_devops_pipeline? def run_auto_devops_pipeline?
...@@ -43,6 +37,20 @@ module Projects ...@@ -43,6 +37,20 @@ module Projects
private private
def validate!
unless valid_visibility_level_change?(project, params[:visibility_level])
raise ValidationError.new('New visibility level not allowed!')
end
if renaming_project_with_container_registry_tags?
raise ValidationError.new('Cannot rename project because it contains container registry tags!')
end
if changing_default_branch?
raise ValidationError.new("Could not set the default branch") unless project.change_head(params[:default_branch])
end
end
def after_update def after_update
todos_features_changes = %w( todos_features_changes = %w(
issues_access_level issues_access_level
...@@ -67,7 +75,7 @@ module Projects ...@@ -67,7 +75,7 @@ module Projects
update_pages_config if changing_pages_https_only? update_pages_config if changing_pages_https_only?
end end
def validation_failed! def update_failed!
model_errors = project.errors.full_messages.to_sentence model_errors = project.errors.full_messages.to_sentence
error_message = model_errors.presence || 'Project could not be updated!' error_message = model_errors.presence || 'Project could not be updated!'
...@@ -89,7 +97,7 @@ module Projects ...@@ -89,7 +97,7 @@ module Projects
end end
def enabling_wiki? def enabling_wiki?
return false if @project.wiki_enabled? return false if project.wiki_enabled?
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end end
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.form-check .form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input' = f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.label :hashed_storage_enabled, class: 'form-check-label' do = f.label :hashed_storage_enabled, class: 'form-check-label' do
Create new projects using hashed storage paths Use hashed storage paths for newly created and renamed projects
.form-text.text-muted .form-text.text-muted
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance. repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
......
...@@ -5,13 +5,13 @@ class ProjectMigrateHashedStorageWorker ...@@ -5,13 +5,13 @@ class ProjectMigrateHashedStorageWorker
LEASE_TIMEOUT = 30.seconds.to_i LEASE_TIMEOUT = 30.seconds.to_i
def perform(project_id) def perform(project_id, old_disk_path = nil)
project = Project.find_by(id: project_id) project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete? return if project.nil? || project.pending_delete?
uuid = lease_for(project_id).try_obtain uuid = lease_for(project_id).try_obtain
if uuid if uuid
::Projects::HashedStorageMigrationService.new(project, logger).execute ::Projects::HashedStorageMigrationService.new(project, old_disk_path || project.full_path, logger: logger).execute
else else
false false
end end
......
---
title: Enable hashed storage for all newly created or renamed projects
merge_request: 19747
author:
type: changed
...@@ -73,7 +73,7 @@ by another folder with the next 2 characters. They are both stored in a special ...@@ -73,7 +73,7 @@ by another folder with the next 2 characters. They are both stored in a special
### How to migrate to Hashed Storage ### How to migrate to Hashed Storage
In GitLab, go to **Admin > Settings**, find the **Repository Storage** section In GitLab, go to **Admin > Settings**, find the **Repository Storage** section
and select "_Create new projects using hashed storage paths_". and select "_Use hashed storage paths for newly created and renamed projects_".
To migrate your existing projects to the new storage type, check the specific To migrate your existing projects to the new storage type, check the specific
[rake tasks]. [rake tasks].
......
...@@ -459,20 +459,6 @@ module EE ...@@ -459,20 +459,6 @@ module EE
end end
alias_method :merge_requests_ff_only_enabled?, :merge_requests_ff_only_enabled alias_method :merge_requests_ff_only_enabled?, :merge_requests_ff_only_enabled
override :rename_repo
def rename_repo
super
path_was = previous_changes['path'].first
old_path_with_namespace = File.join(namespace.full_path, path_was)
::Geo::RepositoryRenamedEventStore.new(
self,
old_path: path_was,
old_path_with_namespace: old_path_with_namespace
).create
end
override :disabled_services override :disabled_services
def disabled_services def disabled_services
strong_memoize(:disabled_services) do strong_memoize(:disabled_services) do
...@@ -574,5 +560,16 @@ module EE ...@@ -574,5 +560,16 @@ module EE
::Geo::RepositoryUpdatedService.new(self, source: ::Geo::RepositoryUpdatedEvent::REPOSITORY).execute ::Geo::RepositoryUpdatedService.new(self, source: ::Geo::RepositoryUpdatedEvent::REPOSITORY).execute
::Geo::RepositoryUpdatedService.new(self, source: ::Geo::RepositoryUpdatedEvent::WIKI).execute ::Geo::RepositoryUpdatedService.new(self, source: ::Geo::RepositoryUpdatedEvent::WIKI).execute
end end
override :after_rename_repository
def after_rename_repository(full_path_before, path_before)
super(full_path_before, path_before)
::Geo::RepositoryRenamedEventStore.new(
self,
old_path: path_before,
old_path_with_namespace: full_path_before
).create
end
end end
end end
...@@ -9,8 +9,8 @@ module EE ...@@ -9,8 +9,8 @@ module EE
super do super do
::Geo::HashedStorageAttachmentsEventStore.new( ::Geo::HashedStorageAttachmentsEventStore.new(
project, project,
old_attachments_path: old_path, old_attachments_path: old_disk_path,
new_attachments_path: new_path new_attachments_path: new_disk_path
).create ).create
end end
end end
......
...@@ -2,11 +2,11 @@ require 'spec_helper' ...@@ -2,11 +2,11 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do describe Projects::HashedStorage::MigrateAttachmentsService do
let(:project) { create(:project, storage_version: 1) } let(:project) { create(:project, storage_version: 1) }
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) }
let(:old_attachments_path) { legacy_storage.disk_path } let(:old_attachments_path) { legacy_storage.disk_path }
let(:new_attachments_path) { hashed_storage.disk_path } let(:new_attachments_path) { hashed_storage.disk_path }
let(:service) { described_class.new(project, old_attachments_path) }
describe '#execute' do describe '#execute' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
......
...@@ -2,9 +2,9 @@ require 'spec_helper' ...@@ -2,9 +2,9 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do describe Projects::HashedStorage::MigrateRepositoryService do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) } let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
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) }
let(:service) { described_class.new(project, legacy_storage.disk_path) }
describe '#execute' do describe '#execute' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
......
...@@ -30,7 +30,7 @@ module Gitlab ...@@ -30,7 +30,7 @@ module Gitlab
end end
end end
# Flag a project to me migrated # Flag a project to be migrated
# #
# @param [Object] project that will be migrated # @param [Object] project that will be migrated
def migrate(project) def migrate(project)
......
...@@ -6,11 +6,11 @@ module QA ...@@ -6,11 +6,11 @@ module QA
view 'app/views/admin/application_settings/_repository_storage.html.haml' do view 'app/views/admin/application_settings/_repository_storage.html.haml' do
element :submit, "submit 'Save changes'" element :submit, "submit 'Save changes'"
element :hashed_storage, element :hashed_storage,
'Create new projects using hashed storage paths' 'Use hashed storage paths for newly created and renamed projects'
end end
def enable_hashed_storage def enable_hashed_storage
check 'Create new projects using hashed storage paths' check 'Use hashed storage paths for newly created and renamed projects'
end end
def save_settings def save_settings
......
...@@ -3444,6 +3444,19 @@ describe Project do ...@@ -3444,6 +3444,19 @@ describe Project do
allow(project).to receive(:previous_changes).and_return('path' => ['foo']) allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
end end
context 'migration to hashed storage' do
it 'calls HashedStorageMigrationService with correct options' do
project = create(:project, :repository, :legacy_storage)
allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
expect_next_instance_of(::Projects::HashedStorageMigrationService) do |service|
expect(service).to receive(:execute).and_return(true)
end
project.rename_repo
end
end
it 'renames a repository' do it 'renames a repository' do
stub_container_registry_config(enabled: false) stub_container_registry_config(enabled: false)
...@@ -3490,8 +3503,10 @@ describe Project do ...@@ -3490,8 +3503,10 @@ describe Project do
context 'when not rolled out' do context 'when not rolled out' do
let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) } 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 hashed storage' do
expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project) expect_next_instance_of(Projects::HashedStorage::MigrateAttachmentsService) do |service|
expect(service).to receive(:execute)
end
project.rename_repo project.rename_repo
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do describe Projects::HashedStorage::MigrateAttachmentsService do
subject(:service) { described_class.new(project) } subject(:service) { described_class.new(project, project.full_path, logger: nil) }
let(:project) { create(:project, :legacy_storage) } let(:project) { create(:project, :legacy_storage) }
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) }
let!(:upload) { Upload.find_by(path: file_uploader.upload_path) } let!(:upload) { Upload.find_by(path: file_uploader.upload_path) }
let(:file_uploader) { build(:file_uploader, project: project) } let(:file_uploader) { build(:file_uploader, project: project) }
let(:old_path) { File.join(base_path(legacy_storage), upload.path) } let(:old_disk_path) { File.join(base_path(legacy_storage), upload.path) }
let(:new_path) { File.join(base_path(hashed_storage), upload.path) } let(:new_disk_path) { File.join(base_path(hashed_storage), upload.path) }
context '#execute' do context '#execute' do
context 'when succeeds' do context 'when succeeds' do
it 'moves attachments to hashed storage layout' do it 'moves attachments to hashed storage layout' do
expect(File.file?(old_path)).to be_truthy expect(File.file?(old_disk_path)).to be_truthy
expect(File.file?(new_path)).to be_falsey expect(File.file?(new_disk_path)).to be_falsey
expect(File.exist?(base_path(legacy_storage))).to be_truthy expect(File.exist?(base_path(legacy_storage))).to be_truthy
expect(File.exist?(base_path(hashed_storage))).to be_falsey expect(File.exist?(base_path(hashed_storage))).to be_falsey
expect(FileUtils).to receive(:mv).with(base_path(legacy_storage), base_path(hashed_storage)).and_call_original expect(FileUtils).to receive(:mv).with(base_path(legacy_storage), base_path(hashed_storage)).and_call_original
...@@ -24,8 +25,8 @@ describe Projects::HashedStorage::MigrateAttachmentsService do ...@@ -24,8 +25,8 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
expect(File.exist?(base_path(hashed_storage))).to be_truthy expect(File.exist?(base_path(hashed_storage))).to be_truthy
expect(File.exist?(base_path(legacy_storage))).to be_falsey expect(File.exist?(base_path(legacy_storage))).to be_falsey
expect(File.file?(old_path)).to be_falsey expect(File.file?(old_disk_path)).to be_falsey
expect(File.file?(new_path)).to be_truthy expect(File.file?(new_disk_path)).to be_truthy
end end
end end
...@@ -40,7 +41,7 @@ describe Projects::HashedStorage::MigrateAttachmentsService do ...@@ -40,7 +41,7 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
service.execute service.execute
expect(File.exist?(base_path(hashed_storage))).to be_falsey expect(File.exist?(base_path(hashed_storage))).to be_falsey
expect(File.file?(new_path)).to be_falsey expect(File.file?(new_disk_path)).to be_falsey
end end
end end
......
...@@ -3,10 +3,11 @@ require 'spec_helper' ...@@ -3,10 +3,11 @@ 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, :legacy_storage, :repository, :wiki_repo) } let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
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) }
subject(:service) { described_class.new(project, project.full_path) }
describe '#execute' do describe '#execute' do
before do before do
allow(service).to receive(:gitlab_shell) { gitlab_shell } allow(service).to receive(:gitlab_shell) { gitlab_shell }
......
...@@ -2,14 +2,19 @@ require 'spec_helper' ...@@ -2,14 +2,19 @@ require 'spec_helper'
describe Projects::HashedStorageMigrationService do describe Projects::HashedStorageMigrationService do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) } let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
subject(:service) { described_class.new(project) } let(:logger) { double }
subject(:service) { described_class.new(project, project.full_path, logger: logger) }
describe '#execute' do describe '#execute' do
context 'repository migration' do context 'repository migration' do
let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, subject.logger) } let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, project.full_path, logger: logger) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
expect(Projects::HashedStorage::MigrateRepositoryService).to receive(:new).with(project, subject.logger).and_return(repository_service) expect(Projects::HashedStorage::MigrateRepositoryService)
.to receive(:new)
.with(project, project.full_path, logger: logger)
.and_return(repository_service)
expect(repository_service).to receive(:execute) expect(repository_service).to receive(:execute)
service.execute service.execute
...@@ -24,10 +29,13 @@ describe Projects::HashedStorageMigrationService do ...@@ -24,10 +29,13 @@ describe Projects::HashedStorageMigrationService do
end end
context 'attachments migration' do context 'attachments migration' do
let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, subject.logger) } let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, project.full_path, logger: logger) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
expect(Projects::HashedStorage::MigrateAttachmentsService).to receive(:new).with(project, subject.logger).and_return(attachments_service) expect(Projects::HashedStorage::MigrateAttachmentsService)
.to receive(:new)
.with(project, project.full_path, logger: logger)
.and_return(attachments_service)
expect(attachments_service).to receive(:execute) expect(attachments_service).to receive(:execute)
service.execute service.execute
......
...@@ -249,6 +249,21 @@ describe Projects::UpdateService, '#execute' do ...@@ -249,6 +249,21 @@ describe Projects::UpdateService, '#execute' do
expect(project.errors.messages).to have_key(:base) expect(project.errors.messages).to have_key(:base)
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk') expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end end
context 'when hashed storage enabled' do
before do
stub_application_setting(hashed_storage_enabled: true)
end
it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do
result = update_project(project, admin, path: 'new-path')
expect(result).not_to include(status: :error)
expect(project).to be_valid
expect(project.errors).to be_empty
expect(project.reload.hashed_storage?(:repository)).to be_truthy
end
end
end end
context 'with hashed storage' do context 'with hashed storage' do
......
...@@ -28,7 +28,7 @@ describe ProjectMigrateHashedStorageWorker, :clean_gitlab_redis_shared_state do ...@@ -28,7 +28,7 @@ describe ProjectMigrateHashedStorageWorker, :clean_gitlab_redis_shared_state do
migration_service = spy migration_service = spy
allow(::Projects::HashedStorageMigrationService) allow(::Projects::HashedStorageMigrationService)
.to receive(:new).with(project, subject.logger) .to receive(:new).with(project, project.full_path, logger: subject.logger)
.and_return(migration_service) .and_return(migration_service)
subject.perform(project.id) subject.perform(project.id)
......
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