Commit 2387ef2b authored by Sean McGivern's avatar Sean McGivern Committed by Micaël Bergeron

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
parent 960b6337
...@@ -62,19 +62,27 @@ module UploadsActions ...@@ -62,19 +62,27 @@ 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
return unless uploader = build_uploader
uploader.retrieve_from_store!(params[:filename])
uploader
end
def build_uploader
return unless params[:secret] && params[:filename]
uploader = uploader_class.new(model, secret: params[:secret]) uploader = uploader_class.new(model, secret: params[:secret])
return nil unless uploader.model_valid? return unless uploader.model_valid?
uploader.retrieve_from_store!(params[:filename])
uploader uploader
end end
......
...@@ -32,8 +32,11 @@ class FileUploader < GitlabUploader ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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?
end # Legacy path relative to project.full_path
File.join(dynamic_segment, identifier)
def model_path_segment else
self.class.model_path_segment(@model) File.join(store_dir, identifier)
end
end end
def store_dir def store_dirs
File.join(base_dir, dynamic_segment) {
Store::LOCAL => File.join(base_dir, dynamic_segment),
Store::REMOTE => File.join(base_dir(ObjectStorage::Store::REMOTE), dynamic_segment)
}
end end
def markdown_link def markdown_link
......
...@@ -4,7 +4,7 @@ class NamespaceFileUploader < FileUploader ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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
......
---
title: File uploads in remote storage now support project renaming.
merge_request: 4597
author:
type: fixed
...@@ -11,34 +11,30 @@ describe FileUploader do ...@@ -11,34 +11,30 @@ 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 'legacy storage' do
context 'when rolled out attachments' do it_behaves_like 'builds correct legacy storage paths'
let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
before do context 'uses hashed storage' do
allow(project).to receive(:disk_path).and_return('ca/fe/fe/ed') context 'when rolled out attachments' do
end let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
it_behaves_like 'builds correct paths', include_examples 'builds correct paths',
store_dir: %r{ca/fe/fe/ed/\h+}, store_dir: %r{@hashed/\h{2}/\h{2}/\h+},
absolute_path: %r{#{described_class.root}/ca/fe/fe/ed/secret/foo.jpg} upload_path: %r{\h+/<filename>}
end end
context 'when only repositories are rolled out' do context 'when only repositories are rolled out' do
let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) } let(:project) { build_stubbed(:project, namespace: group, name: 'project', storage_version: Project::HASHED_STORAGE_FEATURES[:repository]) }
it_behaves_like 'builds correct legacy storage paths' it_behaves_like 'builds correct legacy storage paths'
end
end end
end end
context 'legacy storage' do
it_behaves_like 'builds correct legacy storage paths'
include_examples 'uses hashed storage'
end
context 'object store is remote' do context 'object store is remote' do
before do before do
stub_uploads_object_storage stub_uploads_object_storage
...@@ -46,8 +42,10 @@ describe FileUploader 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
......
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