Merge branch 'poc-upload-hashing-path' into 'master'
File uploads on objects storage should use hashed storage Closes #4952 See merge request gitlab-org/gitlab-ee!4597
This commit is contained in:
parent
960b6337cb
commit
2387ef2b4a
6 changed files with 74 additions and 51 deletions
|
@ -62,22 +62,30 @@ module UploadsActions
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_uploader_from_upload
|
def build_uploader_from_upload
|
||||||
return nil unless params[:secret] && params[:filename]
|
return unless uploader = build_uploader
|
||||||
|
|
||||||
upload_path = uploader_class.upload_path(params[:secret], params[:filename])
|
upload_paths = uploader.upload_paths(params[:filename])
|
||||||
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_path)
|
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_paths)
|
||||||
upload&.build_uploader
|
upload&.build_uploader
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_uploader_from_params
|
def build_uploader_from_params
|
||||||
uploader = uploader_class.new(model, secret: params[:secret])
|
return unless uploader = build_uploader
|
||||||
|
|
||||||
return nil unless uploader.model_valid?
|
|
||||||
|
|
||||||
uploader.retrieve_from_store!(params[:filename])
|
uploader.retrieve_from_store!(params[:filename])
|
||||||
uploader
|
uploader
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_uploader
|
||||||
|
return unless params[:secret] && params[:filename]
|
||||||
|
|
||||||
|
uploader = uploader_class.new(model, secret: params[:secret])
|
||||||
|
|
||||||
|
return unless uploader.model_valid?
|
||||||
|
|
||||||
|
uploader
|
||||||
|
end
|
||||||
|
|
||||||
def image_or_video?
|
def image_or_video?
|
||||||
uploader && uploader.exists? && uploader.image_or_video?
|
uploader && uploader.exists? && uploader.image_or_video?
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,8 +32,11 @@ class FileUploader < GitlabUploader
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.base_dir(model)
|
def self.base_dir(model, store = Store::LOCAL)
|
||||||
model_path_segment(model)
|
decorated_model = model
|
||||||
|
decorated_model = Storage::HashedProject.new(model) if store == Store::REMOTE
|
||||||
|
|
||||||
|
model_path_segment(decorated_model)
|
||||||
end
|
end
|
||||||
|
|
||||||
# used in migrations and import/exports
|
# used in migrations and import/exports
|
||||||
|
@ -51,21 +54,24 @@ class FileUploader < GitlabUploader
|
||||||
#
|
#
|
||||||
# Returns a String without a trailing slash
|
# Returns a String without a trailing slash
|
||||||
def self.model_path_segment(model)
|
def self.model_path_segment(model)
|
||||||
if model.hashed_storage?(:attachments)
|
case model
|
||||||
model.disk_path
|
when Storage::HashedProject then model.disk_path
|
||||||
else
|
else
|
||||||
model.full_path
|
model.hashed_storage?(:attachments) ? model.disk_path : model.full_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.upload_path(secret, identifier)
|
|
||||||
File.join(secret, identifier)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.generate_secret
|
def self.generate_secret
|
||||||
SecureRandom.hex
|
SecureRandom.hex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def upload_paths(filename)
|
||||||
|
[
|
||||||
|
File.join(secret, filename),
|
||||||
|
File.join(base_dir(Store::REMOTE), secret, filename)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
attr_accessor :model
|
attr_accessor :model
|
||||||
|
|
||||||
def initialize(model, mounted_as = nil, **uploader_context)
|
def initialize(model, mounted_as = nil, **uploader_context)
|
||||||
|
@ -75,8 +81,10 @@ class FileUploader < GitlabUploader
|
||||||
apply_context!(uploader_context)
|
apply_context!(uploader_context)
|
||||||
end
|
end
|
||||||
|
|
||||||
def base_dir
|
# enforce the usage of Hashed storage when storing to
|
||||||
self.class.base_dir(@model)
|
# remote store as the FileMover doesn't support OS
|
||||||
|
def base_dir(store = nil)
|
||||||
|
self.class.base_dir(@model, store || object_store)
|
||||||
end
|
end
|
||||||
|
|
||||||
# we don't need to know the actual path, an uploader instance should be
|
# we don't need to know the actual path, an uploader instance should be
|
||||||
|
@ -86,15 +94,19 @@ class FileUploader < GitlabUploader
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_path
|
def upload_path
|
||||||
self.class.upload_path(dynamic_segment, identifier)
|
if file_storage?
|
||||||
|
# Legacy path relative to project.full_path
|
||||||
|
File.join(dynamic_segment, identifier)
|
||||||
|
else
|
||||||
|
File.join(store_dir, identifier)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def model_path_segment
|
def store_dirs
|
||||||
self.class.model_path_segment(@model)
|
{
|
||||||
end
|
Store::LOCAL => File.join(base_dir, dynamic_segment),
|
||||||
|
Store::REMOTE => File.join(base_dir(ObjectStorage::Store::REMOTE), dynamic_segment)
|
||||||
def store_dir
|
}
|
||||||
File.join(base_dir, dynamic_segment)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def markdown_link
|
def markdown_link
|
||||||
|
|
|
@ -4,7 +4,7 @@ class NamespaceFileUploader < FileUploader
|
||||||
options.storage_path
|
options.storage_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.base_dir(model)
|
def self.base_dir(model, _store = nil)
|
||||||
File.join(options.base_dir, 'namespace', model_path_segment(model))
|
File.join(options.base_dir, 'namespace', model_path_segment(model))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class NamespaceFileUploader < FileUploader
|
||||||
def store_dirs
|
def store_dirs
|
||||||
{
|
{
|
||||||
Store::LOCAL => File.join(base_dir, dynamic_segment),
|
Store::LOCAL => File.join(base_dir, dynamic_segment),
|
||||||
Store::REMOTE => File.join('namespace', model_path_segment, dynamic_segment)
|
Store::REMOTE => File.join('namespace', self.class.model_path_segment(model), dynamic_segment)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ class PersonalFileUploader < FileUploader
|
||||||
options.storage_path
|
options.storage_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.base_dir(model)
|
def self.base_dir(model, _store = nil)
|
||||||
File.join(options.base_dir, model_path_segment(model))
|
File.join(options.base_dir, model_path_segment(model))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class PersonalFileUploader < FileUploader
|
||||||
def store_dirs
|
def store_dirs
|
||||||
{
|
{
|
||||||
Store::LOCAL => File.join(base_dir, dynamic_segment),
|
Store::LOCAL => File.join(base_dir, dynamic_segment),
|
||||||
Store::REMOTE => File.join(model_path_segment, dynamic_segment)
|
Store::REMOTE => File.join(self.class.model_path_segment(model), dynamic_segment)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
5
ee/changelogs/unreleased/poc-upload-hashing-path.yml
Normal file
5
ee/changelogs/unreleased/poc-upload-hashing-path.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: File uploads in remote storage now support project renaming.
|
||||||
|
merge_request: 4597
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -11,32 +11,28 @@ describe FileUploader do
|
||||||
shared_examples 'builds correct legacy storage paths' do
|
shared_examples 'builds correct legacy storage paths' do
|
||||||
include_examples 'builds correct paths',
|
include_examples 'builds correct paths',
|
||||||
store_dir: %r{awesome/project/\h+},
|
store_dir: %r{awesome/project/\h+},
|
||||||
|
upload_path: %r{\h+/<filename>},
|
||||||
absolute_path: %r{#{described_class.root}/awesome/project/secret/foo.jpg}
|
absolute_path: %r{#{described_class.root}/awesome/project/secret/foo.jpg}
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'uses hashed storage' do
|
|
||||||
context 'when rolled out attachments' do
|
|
||||||
let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(project).to receive(:disk_path).and_return('ca/fe/fe/ed')
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'builds correct paths',
|
|
||||||
store_dir: %r{ca/fe/fe/ed/\h+},
|
|
||||||
absolute_path: %r{#{described_class.root}/ca/fe/fe/ed/secret/foo.jpg}
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when only repositories are rolled out' do
|
|
||||||
let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) }
|
|
||||||
|
|
||||||
it_behaves_like 'builds correct legacy storage paths'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'legacy storage' do
|
context 'legacy storage' do
|
||||||
it_behaves_like 'builds correct legacy storage paths'
|
it_behaves_like 'builds correct legacy storage paths'
|
||||||
include_examples 'uses hashed storage'
|
|
||||||
|
context 'uses hashed storage' do
|
||||||
|
context 'when rolled out attachments' do
|
||||||
|
let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
|
||||||
|
|
||||||
|
include_examples 'builds correct paths',
|
||||||
|
store_dir: %r{@hashed/\h{2}/\h{2}/\h+},
|
||||||
|
upload_path: %r{\h+/<filename>}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when only repositories are rolled out' do
|
||||||
|
let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) }
|
||||||
|
|
||||||
|
it_behaves_like 'builds correct legacy storage paths'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'object store is remote' do
|
context 'object store is remote' do
|
||||||
|
@ -46,8 +42,10 @@ describe FileUploader do
|
||||||
|
|
||||||
include_context 'with storage', described_class::Store::REMOTE
|
include_context 'with storage', described_class::Store::REMOTE
|
||||||
|
|
||||||
it_behaves_like 'builds correct legacy storage paths'
|
# always use hashed storage path for remote uploads
|
||||||
include_examples 'uses hashed storage'
|
it_behaves_like 'builds correct paths',
|
||||||
|
store_dir: %r{@hashed/\h{2}/\h{2}/\h+},
|
||||||
|
upload_path: %r{@hashed/\h{2}/\h{2}/\h+/\h+/<filename>}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'initialize' do
|
describe 'initialize' do
|
||||||
|
|
Loading…
Reference in a new issue